Silverlight之Ria Service访问存储过程

近日朋友提到了在Ria Service中访问存储过程的问题,本以为使用EF简单的实现,结果令人大跌眼镜,复杂程度远不是我想的那么简单,虽然他也没多复杂,但的确花费了我一天的时间来研究解决,废话不多说,直接讲解。

一、创建Silverlight项目(启用RIA Service)

点击OK之后一个Silverlight应用程序就创建好了

二、创建Entity Framework模型

右键Web应用程序,Add(添加),然后选择New Item(添加项)

选择Data(数据)模板,选择ADO.NET Entity Data Model,给文件起个名字,点击添加

下图中第一个选项是从数据库中选择一个数据库,作为EF的数据库源,第二个是创建空的模型,一般选择前者,点击下一步

选择数据库连接字符串,如果当前没有(即列表是空的,或者不是自己需要的)则点击新连接.

新连接如下,选择合适的服务器,输入用户名密码,选择合适的数据库即可:

继续下一步,选择需要的表、视图、存储过程:

点击完成,看到的如下图的就是edmx文件的设计界面:

默认情况下,只会把表和视图进行模型的映射,而存储过程则需要手动添加了。

首先,右键上图中的空白地方,选择Model Browser(模型浏览器,翻译可能有误),会出现右边的窗口,显示了当前模型中的实体类复杂类型、表、视图,存储过程。

点击Stored Procedures节点,点开只会是自己选择的存储过程.

右键存储过程,选择红圈的菜单项(添加函数):

如下所示,第一个是要添加的函数的名称,可以自定义。(在这里说一个禁忌,个人认为是EF的bug,默认的函数名称和存储过程的名字是一样的,如果修改了是有问题的,这样

会导致调用存储过程的地方的存储过程名字会和Function的名字一致,想想肯定是不正确的,因为这个名字是自定义的,当然不存在这样一个存储过程再数据库了,所以如果要自定义这个方法,那么请记住要修改方法体中的存储过程名字和数据库的存储过程名字一致)

第二个就是要映射的存储过程;

第三个是返回的类型,第一个是没有返回值,第二个是返回简单类型(.net内置类型简单值类型等),第三个是复杂类型(自定义类型),第四个是一个集合,

   返回类型要根据存储过程来确定选择哪一个,如果没有返回值则就选择None,如果是返回int,或者string那就选择Scalars,如果返回就像本例子中的,那就得选择复杂类型     了。

第四个按钮可以得到存储过程列的信息(即返回值的列名),可以看下图中的,返回值是两列,该按钮非常有用,之所以这么说是因为创建的复杂类型的属性是依据该列表中的列来创建的,所以,如果发现列表中的列和我们存储过程的列不一致,则要点击更新了。另外一个很重要的东西,就是属性的类型,必须确保属性的类型和存储过程的字段的类型匹配,

否则会出现错误。

第五个按钮是生成复杂类型,此按钮仅当返回类型为复杂类型时候有效

简单类型选项如下:

下面开始说本步骤最重要的地方了,那就是返回类型,返回类型的选择在上边已经说过,不在赘述,在这里主要说返回复杂类型,因为简单类型是不会有问题的,复杂类型往往就是说返回的结果不能再实体中找到对应的(即列可能是拼凑的,或者是经过一系列运算得到的结果)并且是多列的,所以需要适用于给复杂类型来完成。

点击"创建复杂类型"(Create New Complex Type),点击之后可以看到,Complex后边的下拉框中多出来一个类型,就是点击按钮创建的复杂类型,这个复杂类型可以在.cs文件中看到,当然也可以在模型浏览器中看到,待会说。点击OK,一个函数就被创建好了

打开后台代码看看创建了什么东西:

第一个就是我们的复杂类型,不用管他到底有多复杂,只知道它给我们创建了两个属性,分别是存储过程返回的列,切忌名字必须和存储过程的保持一致,只所以这么做

是因为在执行存储过程的时候也是按照SqlDataReader的方式去读取的,如果名字不一致,则会出错的。

 [EdmComplexTypeAttribute(NamespaceName="LROSModel", Name="proc_room_date_Result")]
[DataContractAttribute(IsReference=true)]
[Serializable()]
public partial class proc_room_date_Result : ComplexObject
{
#region Factory Method

/// <summary>
/// Create a new proc_room_date_Result object.
/// </summary>
/// <param name="currentDate">Initial value of the CurrentDate property.</param>
public static proc_room_date_Result Createproc_room_date_Result(global::System.DateTime currentDate)
{
proc_room_date_Result proc_room_date_Result = new proc_room_date_Result();
proc_room_date_Result.CurrentDate = currentDate;
return proc_room_date_Result;
}

#endregion
#region Primitive Properties

/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=true)]
[DataMemberAttribute()]
public Nullable<global::System.Int32> TimeCount
{
get
{
return _TimeCount;
}
set
{
OnTimeCountChanging(value);
ReportPropertyChanging("TimeCount");
_TimeCount = StructuralObject.SetValidValue(value);
ReportPropertyChanged("TimeCount");
OnTimeCountChanged();
}
}
private Nullable<global::System.Int32> _TimeCount;
partial void OnTimeCountChanging(Nullable<global::System.Int32> value);
partial void OnTimeCountChanged();

/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
[DataMemberAttribute()]
public global::System.DateTime CurrentDate
{
get
{
return _CurrentDate;
}
set
{
OnCurrentDateChanging(value);
ReportPropertyChanging("CurrentDate");
_CurrentDate = StructuralObject.SetValidValue(value);
ReportPropertyChanged("CurrentDate");
OnCurrentDateChanged();
}
}
private global::System.DateTime _CurrentDate;
partial void OnCurrentDateChanging(global::System.DateTime value);
partial void OnCurrentDateChanged();

#endregion
}


第二就是创建的函数了:

函数看起来很长,仔细看,发现主要代码就那么几行,把参数封装成了Parameter(如果有参数),然后执行存储过程,在这里注意,返回值是ObjectResult泛型类型,

不用担心可以直接转为IQuerable的,如果是简单的,那么这里的泛型也就是对应的简单类型。

  public ObjectResult<proc_room_date_Result> proc_room_date(Nullable<global::System.Double> roomid, Nullable<global::System.DateTime> startdate, global::System.String type)
{
ObjectParameter roomidParameter;
if (roomid.HasValue)
{
roomidParameter = new ObjectParameter("roomid", roomid);
}
else
{
roomidParameter = new ObjectParameter("roomid", typeof(global::System.Double));
}

ObjectParameter startdateParameter;
if (startdate.HasValue)
{
startdateParameter = new ObjectParameter("startdate", startdate);
}
else
{
startdateParameter = new ObjectParameter("startdate", typeof(global::System.DateTime));
}

ObjectParameter typeParameter;
if (type != null)
{
typeParameter = new ObjectParameter("type", type);
}
else
{
typeParameter = new ObjectParameter("type", typeof(global::System.String));
}

return base.ExecuteFunction<proc_room_date_Result>("proc_room_date", roomidParameter, startdateParameter, typeParameter);
}

第三步,创建Domainservice,同样右键Web应用程序,添加、新建项,选择web模板,选择Domain Service Class,点击添加

悲剧发生了,原本我们要选择一个EF模型作为Domainservice的数据源,可是发现,这个列表是空的,并且我们明明刚才创建了edmx,那是为什么呢,那是因为没有重新生成 ,哈哈哈,生成后继续,添加

选择要操作的表(应该是全选),点击OK,一个Domainservice就创建完毕了

可以看到这个类没什么特殊的,下面开始进行最后一个关键的步骤:

记得刚才在edmx中创建了一个Function,创建的目的是希望Domainservice可以来调用,没错,现在Domainservice中添加一个函数:

    public IQueryable<proc_room_date_Result> GetRoomDate(int roomId, DateTime startDate, string type)
{
returnObjectContext.proc_room_date(roomId, startDate, type).AsQueryable<proc_room_date_Result>();
        }

方法根据函数的返回值来确定泛型的类型

然后将自定义的类型添加到maetadate文件中,可以看到上图中的复选框的介绍,由于我们的自定义类型没有是手动添加的,所以不能进行直接映射,所以需要手动添加到该文件

(该类型的名称和复杂类型的必须一致,并且是一个部分类,属性也应该和复杂类型的对应)

找到Domainservice对应的metadata文件,打开:

看下方法的的格式:

首先MetadataTypeAttribute特性是必须的,然后就是一个部分类,类型和在edmx中的自定义类型名称一样,然后在部分类中有一个密封类,名字的命名规则通常是

自定义类型名称+Metadata,然后在该密封类中添加属性,属性和edmx中的属性一样,最重要的一点,需要指定一个属性作为key,这里指定TimeCount为key,如果不指定

则会编译出错。

  [MetadataTypeAttribute(typeof(proc_room_date_Result.proc_room_date_ResultMetadata))]
public partial class proc_room_date_Result
{

internal sealed class proc_room_date_ResultMetadata
{

public proc_room_date_ResultMetadata()
{
}
/// <summary>
/// No Metadata Documentation available.
/// </summary>

[Key]
public Int32 TimeCount
{
get;
set;
}

public string CurrentDate { get; set; }
}

}

ok,到此整个WCF RIA Service端的工作已经完成,开始Client的调用

测试成功:

  RoomDomainContext roomservice = new RoomDomainContext();

LoadOperation<proc_room_date_Result> operation = roomservice.Load <proc_room_date_Result >( roomservice.GetRoomDateQuery(2, DateTime.Parse("2011-12-28"), "month"));


operation.Completed += delegate
{
int counts = operation.Entities.Count();
MessageBox.Show(operation.Entities.First().TimeCount.ToString());
};


到此,整个WCF Ria Service 访问存储过程的步骤全部完毕了,希望大家多提意见。

原文地址:https://www.cnblogs.com/ListenFly/p/2284849.html