预备知识:
1、了解反射技术
2、了解C#3.0中扩展方法,分布类,Linq to object,Linq to sql
3、了解ASP.NET MVC
在项目中每添加一个表往往都要添加一套增删改代码,而且这些代码很多情况下都很相似,这里我们给出一个通用的解决方案供大家参考。
一、准备工作:
这里我们先要在数据库中添加两个表News和User如下图:然后拖到dbml中生成实体类。
这里我们先准备一个接口:ICommonTable
Code
public interface ICommonTable
{
int id { get; set; }
}
然后让News和User实体都继承于此接口
Code
public partial class News : ICommonTable
{
}
public partial class User : ICommonTable
{
}
二、通用删除操作
分别添加NewsList.aspx和UserList.aspx两个view,添加方式参见ASP.NET MVC实践系列2-简单应用
在这两个View中加入删除链接:
<%= Html.ActionLink("删除", "Delete", new { key = item.id, partialName="News" })%>
和
<%= Html.ActionLink("删除", "Delete", new { key = item.id, partialName="User" })%>
然后添加一个Controller:
public
ActionResult Delete(
string
partialName,
int
?
key)
{
RepositoryBase repositoryBase
=
new
RepositoryBase(partialName);
repositoryBase.Delete(key
??
0
);
return
RedirectToAction(partialName
+
"
List
"
);
//
返回到list
}
接下来我们介绍一下RepositoryBase :
public
class
RepositoryBase
{
public
Type EntityType {
get
;
private
set
; }
public
RepositoryBase(
string
entityType)
{
Type type
=
GetBllTypeByName(entityType);
EntityType
=
type;
}
public
ICommonTable CreateNew()
{
return
(ICommonTable)Activator.CreateInstance(EntityType);
}
///
<summary>
///
通过字符串获得其Type
///
</summary>
///
<param name="typeName"></param>
///
<returns></returns>
private
static
Type GetBllTypeByName(
string
typeName)
{
Type type
=
null
;
var ass
=
AppDomain.CurrentDomain.GetAssemblies()
.Where(p
=>
p.FullName.Contains(
"
CommonCEDemo
"
));
foreach
(var a
in
ass)
{
type
=
a.GetTypes().Where(p
=>
p.Name
==
typeName).FirstOrDefault();
if
(type
!=
null
)
break
;
}
if
(type
==
null
)
{
throw
new
Exception(
"
类型未定义:
"
+
typeName);
}
return
type;
}
public
RepositoryBase(Type entityType)
{
EntityType
=
entityType;
}
public
ICommonTable Get(
int
id)
{
DBDataContext db
=
Context.GetContext();
return
db.GetTable(EntityType).Cast
<
ICommonTable
>
().FirstOrDefault(p
=>
p.id
==
id);
}
public
void
Delete(
int
id)
{
ICommonTable bllTable
=
Get(id);
Context.GetContext().GetTable(EntityType).DeleteOnSubmit(bllTable);
Context.GetContext().SubmitChanges();
}
}
这里边重点要理解的就是GetBllTypeByName方法。有了这个方法我们就可以动态的通过名字获得相应的Type了。这里还有个问题就是DataContext是从何而来的,我们这里为了简单起见全程声明了一个DataContext没有考虑多线程的情况
public
class
Context
{
static
DBDataContext context;
static
Context()
{
if
(context
==
null
)
{
context
=
new
DBDataContext();
}
}
public
static
DBDataContext GetContext()
{
return
context;
}
}
有个这些当我们想要对一个表进行删除是只要添加相应的链接就可以了(如<%= Html.ActionLink("删除", "Delete", new { key = item.id, partialName="News" })%>)
三、通用增加、修改
首先添加一个CreateEditView.aspx视图
<
asp:Content ID
=
"
Content2
"
ContentPlaceHolderID
=
"
MainContent
"
runat
=
"
server
"
>
<%
Html.RenderPartial(ViewData[
"
PartialName
"
].ToString());
%>
</
asp:Content
>
然后添加两个Partial视图News.ascx和User.ascx,这两个视图是分别基于News和User类的强类型视图,具体内容参加源码。
接下来我们添加相应的Controller
public
ActionResult CreateEditView(
string
partialName,
int
?
key)
{
ViewData[
"
PartialName
"
]
=
partialName;
RepositoryBase repositoryBase
=
new
RepositoryBase(partialName);
ICommonTable table;
if
(key
==
null
)
{
table
=
repositoryBase.CreateNew();
}
else
{
table
=
repositoryBase.Get(key
??
0
);
}
return
View(
"
CreateEditView
"
, table);
}
[AcceptVerbs(HttpVerbs.Post)]
public
ActionResult CreateEditView(
string
partialName,
int
?
key, FormCollection formCollection)
{
RepositoryBase repositoryBase
=
new
RepositoryBase(partialName);
ICommonTable bllTable;
if
(key
==
null
)
{
bllTable
=
repositoryBase.CreateNew();
}
else
{
bllTable
=
repositoryBase.Get(key
??
0
);
}
this
.UpdateModel(bllTable,
true
);
if
(key
==
null
)
{
Context.GetContext().GetTable(repositoryBase.EntityType).InsertOnSubmit(bllTable);
}
Context.GetContext().SubmitChanges();
return
RedirectToAction(partialName
+
"
List
"
);
//
返回到list
}
这里边大家可能有疑问的就是this.UpdateModel(bllTable, true);这个方法在mvc框架中并不存在,这是我添加的扩展方法,这个地方如果使用UpdateModel(bllTable)虽然编译不会报错,但也没有更新成功,查了一下mvc的源码,问题就出在如下源码中:
protected
internal
bool
TryUpdateModel
<
TModel
>
(TModel model,
string
prefix,
string
[] includeProperties,
string
[] excludeProperties, IDictionary
<
string
, ValueProviderResult
>
valueProvider)
where
TModel :
class
{
if
(model
==
null
) {
throw
new
ArgumentNullException(
"
model
"
);
}
if
(valueProvider
==
null
) {
throw
new
ArgumentNullException(
"
valueProvider
"
);
}
Predicate
<
string
>
propertyFilter
=
propertyName
=>
BindAttribute.IsPropertyAllowed(propertyName, includeProperties, excludeProperties);
IModelBinder binder
=
Binders.GetBinder(
typeof
(TModel));
ModelBindingContext bindingContext
=
new
ModelBindingContext() {
Model
=
model,
ModelName
=
prefix,
ModelState
=
ModelState,
ModelType
=
typeof
(TModel),
PropertyFilter
=
propertyFilter,
ValueProvider
=
valueProvider
};
binder.BindModel(ControllerContext, bindingContext);
return
ModelState.IsValid;
}
这个typeof(TModel)造成了只会更新声明类型中有的属性,把它换成model.GetType()就可以解决问题了,我扩这的这个方法如下
public
static
class
ControllerExtension
{
///
<summary>
///
更新时是否按照当前类型进行更新
///
</summary>
///
<typeparam name="TModel"></typeparam>
///
<param name="controller"></param>
///
<param name="model"></param>
///
<param name="isEx"></param>
public
static
void
UpdateModel
<
TModel
>
(
this
Controller controller, TModel model,
bool
isExtension)
where
TModel :
class
{
if
(isExtension)
{
Predicate
<
string
>
propertyFilter
=
propertyName
=>
IsPropertyAllowed(propertyName,
null
,
null
);
IModelBinder binder
=
ModelBinders.Binders.GetBinder(model.GetType());
ModelBindingContext bindingContext
=
new
ModelBindingContext()
{
Model
=
model,
ModelName
=
null
,
ModelState
=
controller.ModelState,
ModelType
=
model.GetType(),
PropertyFilter
=
propertyFilter,
ValueProvider
=
controller.ValueProvider
};
binder.BindModel(controller.ControllerContext, bindingContext);
}
else
{
throw
new
Exception(
"
isExtension不能选择false
"
);
}
}
private
static
bool
IsPropertyAllowed(
string
propertyName,
string
[] includeProperties,
string
[] excludeProperties)
{
bool
includeProperty
=
(includeProperties
==
null
)
||
(includeProperties.Length
==
0
)
||
includeProperties.Contains(propertyName, StringComparer.OrdinalIgnoreCase);
bool
excludeProperty
=
(excludeProperties
!=
null
)
&&
excludeProperties.Contains(propertyName, StringComparer.OrdinalIgnoreCase);
return
includeProperty
&&
!
excludeProperty;
}
}
有了这些,当我们想对新表进行编辑和添加时只需要添加相应的Partial编辑视图就可以了,简化了我们的编程工作。
四、缺点
1、须要按照规则命名,比方说Partial视图需要以相应的类名来命名
2、页面引用是弱类型的