你的位置:首页 > ASP.net教程

[ASP.net教程]EntityFramework4.1开发


常见问题大概为这几个

  一.ef4.1 codeFirst 修改表结构 增加字段等 EF code first需要重新生成库导致数据丢失的问题.

  二.ef4.1 没有了edmx等复杂的东西 变得简单 干净  但如何使用存储过程,存储过程可以返回表 可以返回数值 也有可能是执行修改 删除 增加等  该怎么做?

  三.ef4.1 如何使用数据库视图?每个视图都要去建立对应的实体类么?有简单的方法么?

  四.ef4.1 如何执行SQL函数等操作?

  五.ef4.1 如何跨数据库访问?

  六.ef4.1执行连接查询?什么时候执行左连接? 什么时候执行内连接? ef 根据什么去判断?

  七.新手使用ef4.1 常见的一些报错信息

  其实这些问题是比较简单的~ 所以此文送给刚用ef4.1的新手~

  下面开始一一解决这些问题

  一.ef4.1 codeFirst 修改表结构 增加字段等 EF code first需要重新生成库导致数据丢失的问题

  说这个问题前 首先先说下 我使用ef4.1 codefirst的目的. 是因为可以有更纯净的POCO 不再有EDMX这些东西  而不是真正的用 code first 先有代码 再生成数据库.所以 我虽然使用

  的是codefirst 但是本质依然是数据库优先.

  所以这个被问的很多的问题 解决办法其实是非常简单的.只要你的数据库已经存在了 那么即使你用code first ef 也不会给你去生成数据库的. 这个时候 你增加表字段 甚至增加表 只要把

  实体类也相应的修改 则数据库里的数据 是不会被清空的.

  说下我的开发步骤  先设计数据库 并建立数据库=>通过EF工具生成映射和实体类=>开发代码     当遇到修改时=>  先修改数据库如添加字段或表等=>再修改实体类=>继续开发

  这样就不会有重新生成数据的烦恼了 而且项目里也不会出现edmx~

  二.ef4.1 没有了edmx等复杂的东西 变得简单 干净  但如何使用存储过程,存储过程可以返回表 可以返回数值 也有可能是执行修改 删除 增加等  该怎么做?

  说这个问题前 依然先说下我的观点.个人认为 既然使用orm框架  就应该把业务逻辑等 都放到业务逻辑层 而不应该再使用存储过程。我更偏重重业务逻辑层 轻存储过程这样的开发~

  再ef4.0里 添加存储过程 比较容易 有edmx 调一调 存储过程就添加上了 但是在ef4.1里 只有干净的poco 不再有edmx了 改怎么办呢?尤其是存储过程可以是查表 查值 或者执行修改删除.

  一个一个来解决

  1.执行返回表类型的存储过程

  先上存储过程 随手写的一个最简单的

Create PROCEDURE [dbo].[ProSelectStu]
@StudentID int
AS
BEGIN

Select Student.* from Enrollment,Student
where Enrollment.StudentID=Student.StudentID
and Enrollment.StudentID=@StudentID

END

GO

  执行存储过程的方法 是用直接执行sql的方式 我在我的文章第九篇 有过详细的介绍~大家可以先去看下

  执行表的存储过程 其实是非常强大的 延迟加载 等都有体现 博客园的陆老师已经写了 写的非常清楚了~我这里就不再写了 大家可以去他那看下 提供个连接~

  EF使用存储过程查询表的

  2.执行返回值的存储过程

  先上存储过程

CREATE PROCEDURE [dbo].[ProSelectCount]
@StuId int
AS
BEGIN
select COUNT(*) from Enrollment where StudentID=@StuId
END

  一个简单的查询数量

  这里用sqlQuery 执行访问 数据库 因为需要提供返回类型 而我们返回的是int 所以先得到int的类型

  3.执行增删改

CREATE PROCEDURE [dbo].[ProDel]
@stuId int,
@courseId int
AS
BEGIN

DELETE FROM [WLFSchool].[dbo].[Enrollment]
where StudentID=@stuId and CourseID=@courseId

END

  这个用的是操作数据库 返回受影响行数

  三.ef4.1 如何使用数据库视图?每个视图都要去建立对应的实体类么?有简单的方法么?

  先说下最传统的方法 只需把视图 当成表 建立对应的实体类  然后加到dbcontext 里即可。没什么难度。

  再说一个问题 使用linq 有个非常美妙的功能 投影映射 和C#3.0的 匿名函数 让我们很多情况 不需要视图的

from c in classes
from s in students
where c.ClassID == s.ClassID
order by c.CreateTime
select new
{
Name = s.Name,
Age = s.Age,
ClassName = c.ClassName
};

  再通过  var result 接受上面的值  这样我们就不用去数据库建视图 不用再建实体类 是不是很省事呢?

  如果公司强大的DBA 已经给我们建好了很多视图 是不是就要一个个去写实体类呢?如果你使用的是C#4.0 那么可以用动态的 来解决这个问题~

  像下面这样使用 是不是很爽

  这个不仅可以查询视图 普通的表 只要是SQL语句 都可以自动生成动态类 让你用~

  下面是扩展方法  和 使用Emit 来动态构建 感谢 ASP.NET 韋 给的帮助~~

public static class DatabaseExtensions
{
public static IEnumerable SqlQueryForDynamic(this Database db,
string sql,
params object[] parameters)
{
IDbConnection defaultConn = new System.Data.SqlClient.SqlConnection();

return SqlQueryForDynamicOtherDB(db, sql, defaultConn, parameters);
}

public static IEnumerable SqlQueryForDynamicOtherDB(this Database db,
string sql,
IDbConnection conn,
params object[] parameters)
{
conn.ConnectionString = db.Connection.ConnectionString;

if (conn.State != ConnectionState.Open)
{
conn.Open();
}

IDbCommand cmd = conn.CreateCommand();
cmd.CommandText = sql;

IDataReader dataReader = cmd.ExecuteReader();

if (!dataReader.Read())
{
return null; //无结果返回Null
}

#region 构建动态字段

TypeBuilder builder = DatabaseExtensions.CreateTypeBuilder(
"EF_DynamicModelAssembly",
"DynamicModule",
"DynamicType");

int fieldCount = dataReader.FieldCount;
for (int i = 0; i < fieldCount; i++)
{
//dic.Add(i, dataReader.GetName(i));

//Type type = dataReader.GetFieldType(i);

DatabaseExtensions.CreateAutoImplementedProperty(
builder,
dataReader.GetName(i),
dataReader.GetFieldType(i));
}

#endregion

dataReader.Close();
dataReader.Dispose();
cmd.Dispose();
conn.Close();
conn.Dispose();

Type returnType = builder.CreateType();

if (parameters != null)
{
return db.SqlQuery(returnType, sql, parameters);
}
else
{
return db.SqlQuery(returnType, sql);
}
}

public static TypeBuilder CreateTypeBuilder(string assemblyName,
string moduleName,
string typeName)
{
TypeBuilder typeBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
new AssemblyName(assemblyName),
AssemblyBuilderAccess.Run).DefineDynamicModule(moduleName).DefineType(typeName,
TypeAttributes.Public);
typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);
return typeBuilder;
}

public static void CreateAutoImplementedProperty(
TypeBuilder builder,
string propertyName,
Type propertyType)
{
const string PrivateFieldPrefix = "m_";
const string GetterPrefix = "get_";
const string SetterPrefix = "set_";

// Generate the field.
FieldBuilder fieldBuilder = builder.DefineField(
string.Concat(
PrivateFieldPrefix, propertyName),
propertyType,
FieldAttributes.Private);

// Generate the property
PropertyBuilder propertyBuilder = builder.DefineProperty(
propertyName,
System.Reflection.PropertyAttributes.HasDefault,
propertyType, null);

// Property getter and setter attributes.
MethodAttributes propertyMethodAttributes = MethodAttributes.Public
| MethodAttributes.SpecialName
| MethodAttributes.HideBySig;

// Define the getter method.
MethodBuilder getterMethod = builder.DefineMethod(
string.Concat(
GetterPrefix, propertyName),
propertyMethodAttributes,
propertyType,
Type.EmptyTypes);

// Emit the IL code.
// ldarg.0
// ldfld,_field
// ret
ILGenerator getterILCode = getterMethod.GetILGenerator();
getterILCode.Emit(OpCodes.Ldarg_0);
getterILCode.Emit(OpCodes.Ldfld, fieldBuilder);
getterILCode.Emit(OpCodes.Ret);

// Define the setter method.
MethodBuilder setterMethod = builder.DefineMethod(
string.Concat(SetterPrefix, propertyName),
propertyMethodAttributes,
null,
new Type[] { propertyType });

// Emit the IL code.
// ldarg.0
// ldarg.1
// stfld,_field
// ret
ILGenerator setterILCode = setterMethod.GetILGenerator();
setterILCode.Emit(OpCodes.Ldarg_0);
setterILCode.Emit(OpCodes.Ldarg_1);
setterILCode.Emit(OpCodes.Stfld, fieldBuilder);
setterILCode.Emit(OpCodes.Ret);

propertyBuilder.SetGetMethod(getterMethod);
propertyBuilder.SetSetMethod(setterMethod);
}
}

  四.ef4.1 如何执行SQL函数等操作?

  添加引用  System.Data.Objects.SqlClient.SqlFunctions 主要是这个命名空间

  使用方法~上一个工作中的例子~

var query = from s in student.T_StudentInfo
where SqlFunctions.DateDiff("day", s.CreateTime, "2011/11/4") == 0
select s.StudentName;

  使用SQL 的datadiff 函数~~

  五.ef4.1 如何跨数据库访问?

  每次别人问我这个问题 毫不犹豫的把站长dudu的文章发过去~ 他已经很好的解决了~

  http://www.cnblogs.com/dudu/archive/2011/03/29/entity_framework_cross_database_query_fact.html

  核心思路 欺骗SQL 利用创建同义词去实现  
  六.ef4.1执行连接查询?什么时候执行左连接? 什么时候执行内连接? ef 根据什么去判断?

  当我们做多表查询时  用Include 强制加载 或用 select 去查询时  发现生成的SQL语句 有时是左连接  有时是inner join。

  其实EF是根据我们实体类的连接字段 是否可空来判断的~比如外键 studentID

  public  Nullable<int> StudentID { get; set; }

  是否可空 就会造成 是 left join 还是 inner join~~
  七.新手使用ef4.1 常见的一些报错信息

  1.执行命令定义时出错

  出现这个错的原因有很多  数据库语句错误 我们可以先通过监测SQL 语句是否发送到数据库 然后执行这条SQL语句 看看是否有问题

  造成这个错的原因 还有可能是 连接对象一直被占用 因为EF有延迟加载 只是select时 并没有真正去数据库执行

  我们可以先把前面的查询语句 tolist等  再去执行下面的操作

  2.

  System.Data.Edm.EdmEntityType: : EntityType“Enrollment”未定义键。请为该 EntityType 定义键。
  System.Data.Edm.EdmEntitySet: EntityType: EntitySet,Enrollments 基于未定义键的类型Enrollment。
  遇到这种情况 尝试给主键加上[Key]

  3.更新条目错误

  依然检测数据库语句 是否有外键约束导致插入错误等