Silverlight下用Ria Services访问多种数据库

  Ria Services 与 linq to Sql, Ado.net Entity Framework 的集合很紧密,但现目前不能支持其他数据库的操作(如:Oracle,my SQL等……),而现在支持多数据库的ORM: DataObjects.Net不错,对Linq的支持最完整的一个ORM了,但是现目前不支持.net framwork 4.0,官方好像正在支持中。。。看了很久,现目前还是只能用NHibernate了,FluentNHibernate为何物,大家可以去官方网站看看http://fluentnhibernate.org/

  

好,那么我们尝试用FluentNHibernate来实现Ria Service,多数据库访问。我们就用Oracle来做实验咯:)

添加一些dll

首先来建立连接Session:

代码
private static ISessionFactory createSessionFactory()
{
FluentConfiguration fluentCfg
= Fluently.Configure()
.Database(
OracleDataClientConfiguration
.Oracle10
.ConnectionString(
c
=> c.Server(ConfigurationManager.AppSettings["DBHost"])//127.0.0.1")
.Instance(ConfigurationManager.AppSettings["DBInstance"])
.Username(ConfigurationManager.AppSettings[
"DBUser"])
.Password(ConfigurationManager.AppSettings[
"DBPwd"])
).QuerySubstitutions(
"true=1,false=0"))
.Mappings(m
=> m.FluentMappings.AddFromAssemblyOf<Department>());

ISessionFactory factory
= null;
try
{
factory
= fluentCfg.BuildSessionFactory();
}
catch (Exception ex)
{
log.Error(ex);
throw new Exception("创建数据库连接出错。",ex);
}
return factory;
}

MS SQL 连接也很方便 

代码
FluentConfiguration fluentSqlCfg = Fluently.Configure()
.Database(
MsSqlConfiguration.MsSql2008.ConnectionString(
cn
=>cn.Server(ConfigurationManager.AppSettings["DBHost"])//127.0.0.1")
.Database(ConfigurationManager.AppSettings["DBInstance"])
.Username(ConfigurationManager.AppSettings[
"DBUser"])
.Password(ConfigurationManager.AppSettings[
"DBPwd"])
).QuerySubstitutions(
"true=1,false=0"))
.Mappings(m
=> m.FluentMappings.AddFromAssemblyOf<Department>());

  

 

  好进入重点,接下来实验下服务器到客服端的元数据转换是怎么样的。

代码
public class Department
{
public int Id { get; set; }

public string Code { get; set; }

public string Name { get; set; }
}


[EnableClientAccess]
public class OrganizationService : DomainService
{
[Query]
public IQueryable<Department> GetAllDepartment()
{
return new List<Department>().AsQueryable();
}

public void AddDepartment(Department depart)
{

}

public void UpdateDepartment(Department depart)
{

}

public void DeleteDepartment(Department department)
{

}
}

编译时会出错,提示为:

原来必须要生有一个KeyAttribute 的标记属性字段。我们加上后,成功了!所在的客户端会自动生成一堆文件代码:(请看下图)

 

  我的的实体据模型肯定会有很多关联组合情况。我们扩展下:

 

代码
public class Department
{
[Key]
public int Id { get; set; }

public string Code { get; set; }

public string Name { get; set; }

public virtual Department Parent { get; set; }
}

public class Person
{
[Key]
public int Id { get; set; }

public string Login { get; set; }

public string Password { get; set; }

public String Code { get; set; }

public String Name { get; set; }

public Department Department { get; set; }

}

我们编译后看看客户端到底是什么:

 

代码
/// <summary>
/// The 'Department' entity class.
/// </summary>
[DataContract(Namespace="http://schemas.datacontract.org/2004/07/Platform.Web.Models")]
public sealed partial class Department : Entity
{

private string _code;

private int _id;

private string _name;

#region Extensibility Method Definitions

/// <summary>
/// This method is invoked from the constructor once initialization is complete and
/// can be used for further object setup.
/// </summary>
partial void OnCreated();
partial void OnCodeChanging(string value);
partial void OnCodeChanged();
partial void OnIdChanging(int value);
partial void OnIdChanged();
partial void OnNameChanging(string value);
partial void OnNameChanged();

#endregion


/// <summary>
/// Initializes a new instance of the <see cref="Department"/> class.
/// </summary>
public Department()
{
this.OnCreated();
}

/// <summary>
/// Gets or sets the 'Code' value.
/// </summary>
[DataMember()]
public string Code
{
get
{
return this._code;
}
set
{
if ((this._code != value))
{
this.OnCodeChanging(value);
this.RaiseDataMemberChanging("Code");
this.ValidateProperty("Code", value);
this._code = value;
this.RaiseDataMemberChanged("Code");
this.OnCodeChanged();
}
}
}

/// <summary>
/// Gets or sets the 'Id' value.
/// </summary>
[DataMember()]
[Editable(
false, AllowInitialValue=true)]
[Key()]
[RoundtripOriginal()]
public int Id
{
get
{
return this._id;
}
set
{
if ((this._id != value))
{
this.OnIdChanging(value);
this.ValidateProperty("Id", value);
this._id = value;
this.RaisePropertyChanged("Id");
this.OnIdChanged();
}
}
}

/// <summary>
/// Gets or sets the 'Name' value.
/// </summary>
[DataMember()]
public string Name
{
get
{
return this._name;
}
set
{
if ((this._name != value))
{
this.OnNameChanging(value);
this.RaiseDataMemberChanging("Name");
this.ValidateProperty("Name", value);
this._name = value;
this.RaiseDataMemberChanged("Name");
this.OnNameChanged();
}
}
}

/// <summary>
/// Computes a value from the key fields that uniquely identifies this entity instance.
/// </summary>
/// <returns>An object instance that uniquely identifies this entity instance.</returns>
public override object GetIdentity()
{
return this._id;
}
}

/// <summary>
/// The 'Person' entity class.
/// </summary>
[DataContract(Namespace="http://schemas.datacontract.org/2004/07/Platform.Web.Models")]
public sealed partial class Person : Entity
{

private string _code;

private int _id;

private string _login;

private string _name;

private string _password;

#region Extensibility Method Definitions

/// <summary>
/// This method is invoked from the constructor once initialization is complete and
/// can be used for further object setup.
/// </summary>
partial void OnCreated();
partial void OnCodeChanging(string value);
partial void OnCodeChanged();
partial void OnIdChanging(int value);
partial void OnIdChanged();
partial void OnLoginChanging(string value);
partial void OnLoginChanged();
partial void OnNameChanging(string value);
partial void OnNameChanged();
partial void OnPasswordChanging(string value);
partial void OnPasswordChanged();

#endregion


/// <summary>
/// Initializes a new instance of the <see cref="Person"/> class.
/// </summary>
public Person()
{
this.OnCreated();
}

/// <summary>
/// Gets or sets the 'Code' value.
/// </summary>
[DataMember()]
public string Code
{
get
{
return this._code;
}
set
{
if ((this._code != value))
{
this.OnCodeChanging(value);
this.RaiseDataMemberChanging("Code");
this.ValidateProperty("Code", value);
this._code = value;
this.RaiseDataMemberChanged("Code");
this.OnCodeChanged();
}
}
}

/// <summary>
/// Gets or sets the 'Id' value.
/// </summary>
[DataMember()]
[Editable(
false, AllowInitialValue=true)]
[Key()]
[RoundtripOriginal()]
public int Id
{
get
{
return this._id;
}
set
{
if ((this._id != value))
{
this.OnIdChanging(value);
this.ValidateProperty("Id", value);
this._id = value;
this.RaisePropertyChanged("Id");
this.OnIdChanged();
}
}
}

/// <summary>
/// Gets or sets the 'Login' value.
/// </summary>
[DataMember()]
public string Login
{
get
{
return this._login;
}
set
{
if ((this._login != value))
{
this.OnLoginChanging(value);
this.RaiseDataMemberChanging("Login");
this.ValidateProperty("Login", value);
this._login = value;
this.RaiseDataMemberChanged("Login");
this.OnLoginChanged();
}
}
}

/// <summary>
/// Gets or sets the 'Name' value.
/// </summary>
[DataMember()]
public string Name
{
get
{
return this._name;
}
set
{
if ((this._name != value))
{
this.OnNameChanging(value);
this.RaiseDataMemberChanging("Name");
this.ValidateProperty("Name", value);
this._name = value;
this.RaiseDataMemberChanged("Name");
this.OnNameChanged();
}
}
}

/// <summary>
/// Gets or sets the 'Password' value.
/// </summary>
[DataMember()]
public string Password
{
get
{
return this._password;
}
set
{
if ((this._password != value))
{
this.OnPasswordChanging(value);
this.RaiseDataMemberChanging("Password");
this.ValidateProperty("Password", value);
this._password = value;
this.RaiseDataMemberChanged("Password");
this.OnPasswordChanged();
}
}
}

/// <summary>
/// Computes a value from the key fields that uniquely identifies this entity instance.
/// </summary>
/// <returns>An object instance that uniquely identifies this entity instance.</returns>
public override object GetIdentity()
{
return this._id;
}
}

客户端的Eitity模型好像少了关联类啊!!!怎么办,赶紧查资料吧。原来关联类需要加属性AssociationAttribute标记。。。加后编译也报错好需要添加一些字段,原来wcf Ria 从客户端传到服务器只传送是关联类实体Id值。好了,我们看看完整的结果吧:

代码
public class Department
{
[Key]
public virtual int Id { get; set; }

[Display(GroupName
= "基本信息", Name = "编码")]
public virtual string Code { get; set; }

[Display(GroupName
= "基本信息", Name = "部门名称")]
public virtual string Name { get; set; }

/// <summary>
/// 是否是外部单位
/// </summary>
public virtual bool IsExtUnits { get; set; }


public virtual int ParentId { get; set; }

private Department _Parent;
/// <summary>
/// 上级部门或单位
/// </summary>
[Association("Department_Parent", "ParentId", "Id", IsForeignKey = true)]
[Display(GroupName
= "基本信息", Name = "上级部门")]
[Include]
public virtual Department Parent
{
get
{
return _Parent;
}
set
{
ParentId
= value == null ? 0 : value.Id;
_Parent
= value;
}
}

/// <summary>
/// 下级部门或单位
/// </summary>
[Association("Department_Parent", "Id", "ParentId")]
public virtual IList<Department> Children { get; set; }

/// <summary>
/// 当前部门或单位的人员
/// </summary>
[Include]
[Association(
"Person_Department", "Id", "DepartmentId")]
public virtual IList<Person> Persons { get; set; }
}

public class Person
{
[Key]
public virtual int Id { get; set; }

[Display(GroupName
= "基本信息", Name = "用户名")]
public virtual string Login { get; set; }

[Display(GroupName
= "基本信息", Name = "密码")]
public virtual string Password { get; set; }

[Display(GroupName
= "基本信息", Name = "工号")]
public virtual String Code { get; set; }

[Display(GroupName
= "基本信息", Name = "姓名")]
public virtual String Name { get; set; }

#region 所在单位

public virtual int DepartmentId
{
get;
set;
}

private Department _department;

[Include]
[Association(
"Person_Department", "DepartmentId", "Id", IsForeignKey = true)]
[Display(GroupName
= "基本信息", Name = "部门")]
public virtual Department Department
{
get
{
return _department;
}
set
{
this._department = value;
DepartmentId
= value == null ? 0 : value.Id;
}
}

#endregion
}

  大家看到实体属性都有了virtual来修饰的,因为用FluentNHibernate构建映射时需要。

 

 

 

  现在看来从客户端到服务器之间的问题解决了,接下来该处理怎么将实体模型保存到数据库的问题了。

用Flunent 来映射很方便,我们在实体类同目录下添加以下代码。我们就直接上代码了。

  

代码
public DepartmentMap()
{
//如果没有Id值 自动生成唯一Id
Id(x => x.Id).GeneratedBy.HiLo("HIBERNATE_UNIQUE_KEY", "NEXT_HI", "1000");
Map(x
=> x.Code);
Map(x
=> x.Name).Not.Nullable();
Map(x
=> x.IsExtUnits);

//外键关联映射
References<Department>(x => x.Parent).Column("Parent_Id");

HasMany
<Department>(x => x.Children).AsList().LazyLoad().KeyColumn("Parent_Id").Inverse();

//Department_Id 是在Person表里面的一个外键,
//.Inverse()这个方法可以确保主表保存后在保存子表
HasMany<Person>(x => x.Persons).KeyColumn("Department_Id").AsBag().Inverse();
}


public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
//实体类对应数据库表名称
Table("Person");
Id(x
=> x.Id).GeneratedBy.HiLo("HIBERNATE_UNIQUE_KEY", "NEXT_HI", "1000");
Map(x
=> x.Code);
Map(x
=> x.Name);
Map(x
=> x.Login);
Map(x
=> x.Password);

//外键 Department_Id在Person表里
References<Department>(x => x.Department).Column("Department_Id");
//HasOne<UserProfile>(x => x.Profile).Cascade.All().PropertyRef(y=>y.User);

}
}

 

 

就这么简单,完成了实体模型到数据库的映射关系。用起来很方便吧。。。。(不过还是没有DataObjects.Net 实体定义方便,直接用Attribute就可以了),接下来我们来实现public class OrganizationService : DomainService 。记住服务类一定要加[EnableClientAccess]标记。

 

代码
[EnableClientAccess]
public class OrganizationService : DomainService
{
/// <summary>
/// 更新,插入都需要检查外接关联关系
/// </summary>
/// <param name="entity"></param>
internal static void EnsureFKValue(Object entity)
{
if (entity == null)
{
return;
}
ISession session
= SessionHelper.GetSession();
if (session == null)
{
return;//log
}

string entityName = entity.GetType().FullName;
//session.SessionFactory.GetClassMetadata
Type entityType = entity.GetType();
PropertyInfo[] properties
= entityType.GetProperties();
foreach (var p in properties)
{
object[] attributes = p.GetCustomAttributes(typeof(AssociationAttribute), true);
if (attributes == null || attributes.Length == 0)
{
continue;
}

AssociationAttribute asso
= attributes[0] as AssociationAttribute;
if (asso.IsForeignKey)
{
PropertyInfo fkProp
= entityType.GetProperty(asso.ThisKey);
if (fkProp == null)
{
continue;
}
int? fkId = fkProp.GetValue(entity, null) as int?;
if (!fkId.HasValue || fkId.Value <= 0)
{
continue;
}

//获取关联的对象
Object fkEntity = session.Get(p.PropertyType, fkId.Value);
if (fkEntity == null)
{
continue;//?
}
p.SetValue(entity, fkEntity,
null);
}
else
{
//?

}
}
}

[Query]
public IQueryable<Department> GetDeparments()
{
var query
= SessionHelper.GetSession().Linq<Department>();
return query;
}

public void AddDepartment(Department department)
{
var session
= SessionHelper.GetSession();
EnsureFKValue(department);
session.Save(department);
session.Flush();
}

public void UpdateDepartment(Department department)
{
var session
= SessionHelper.GetSession();
EnsureFKValue(department);
session.Update(department);
session.Flush();
}

public void DeleteDepartment(Department department)
{
var session
= SessionHelper.GetSession();
var item
= session.Linq<Department>().FirstOrDefault(a => a.Id == department.Id);
if (item != null)
session.Delete(item);
session.Flush();
}


[Query]
public IQueryable<Person> GetAllPerson()
{
var query
= SessionHelper.GetSession().Linq<Person>();
return query;
}

public void AddPerson(Person person)
{
var session
= SessionHelper.GetSession();
EnsureFKValue(person);
session.Save(person);
session.Flush();
}

public void UpdatePerson(Person person)
{
var session
= SessionHelper.GetSession();
EnsureFKValue(person);
session.Update(person);
session.Flush();
}

public void DeletePerson(Person person)
{
var session
= SessionHelper.GetSession();
var item
= session.Linq<Person>().FirstOrDefault(a => a.Id == person.Id);
if (item != null)
session.Delete(item);
session.Flush();
}
}

到此我们就可以用wcf Ria Service 方式进行传输数据方式 实现对不同数据库的操作了。

写到这里,我想也有必要介绍下自定义用户验证,下一节我们再介绍吧,休息。。。

posted @ 2010-05-30 19:36  木吉他-.-  阅读(1211)  评论(5编辑  收藏  举报