当前位置:  编程技术>.net/c#/asp.net
本页文章导读:
    ▪Entity Framework Code First 学习日记(3)      我在上一篇日记的最后已经预告了,我这次将给大家介绍一下如何将Fluent API的配置组织的更好,更利于维护,但是一位“特别”同志迫不及待地揭晓了谜底,那么就让我们来看一下Fluent API的.........
    ▪企业级应用架构(NHibernater+Spring.Net+MVC3)_2.0               本最小架构在1.0版本的基础上进行了进一步改进,适用于B/S结构的企业级系统应用,本系统依然站在巨人的肩膀上(马丁福勒),是在.NET领域中的实现模式.........
    ▪分享懒人张RDLC报表(五、六)      来自蜡人张:RDLC报表(五)        随着Visual Studio 2005中文版的推出,Microsoft汉化了MSDN的大部分内容,开发者再也不用啃英文了,本来想介绍一下LocalReport的Render方法.........

[1]Entity Framework Code First 学习日记(3)
    来源:    发布时间: 2013-10-28

我在上一篇日记的最后已经预告了,我这次将给大家介绍一下如何将Fluent API的配置组织的更好,更利于维护,但是一位“特别”同志迫不及待地揭晓了谜底,那么就让我们来看一下Fluent API的另一种使用方式吧。

我们项目中的domain中一般都有很多的类,如果我们把所有类的代码都写在DbContext子类的OnModelCreating重载方法中,那么这个方法将会非常庞大,并且各个类的配置都混杂在一起,非常不利于项目的后期维护。所以我们就需要更好地组织Fluent API的数据映射配置。

从上一篇日记我们知道modelBuilder的Entity<>泛型方法的返回值是EntityTypeConfiguration<>泛型类。我们可以定义一个继承自EntityTypeConfiguration<>泛型类的类来定义domain中每个类的数据库配置,我们在这个自定义类的构造函数中使用我们上次提到的那些方法配置数据库的映射。

public class CustomerEntityConfiguration:EntityTypeConfiguration<Customer>
    {
        public CustomerEntityConfiguration()
        {
            HasKey(c => c.IDCardNumber).Property(c => c.IDCardNumber).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
            this.Property(c => c.IDCardNumber).HasMaxLength(20);
            this.Property(c => c.CustomerName).IsRequired().HasMaxLength(50);
            this.Property(c => c.Gender).IsRequired().HasMaxLength(1);
            this.Property(c => c.PhoneNumber).HasMaxLength(20);
        }
    }

昨天我们已经介绍了怎样去改变Code First默认的数据库映射。但是当我们通过Fluent API改变数据库映射时,Code First会如何处理与新的数据库映射不匹配的数据库呢?

通过Code First提供的Database类的SetInitializer方法设定Code First如何根据Fluent API数据库映射配置初始化数据库。

每次AppDomain加载的时候会执行SetInitializer指定的初始化方法。

SetInitializer方法的参数可以使以下三个泛型类的对象:

CreateDatabaseIfNotExists<>:只有在没有数据库的时候才会根据数据库连接配置创建新的数据库。这种配置主要用于production环境,因为你不可能把你现在使用的数据库删除掉,那样会损失重要的数据。你需要让你的实施人员拿着与Fluent API配置对应的数据库脚本去更新数据库。

 

DropCreateDatabaseIfModelChanges<>:只要Fluent API配置的数据库映射发生变化或者domain中的model发生变化了,就把以前的数据库删除掉,根据新的配置重新建立数据库。这种方式比较适合开发数据库,可以减少开发人员的工作量。

 

DropCreateDatabaseAlways<>:不管数据库映射或者model是否发生变化,每次都重新删除并根据配置重建数据库。这种方式可以适用于一些特殊情况的测试,比如说当每次测试结束之后把所有的测试数据都删除掉,并且在测试开始前插入一些基础数据。

一般Database.SetInitializer方法都是在应用程序的入口,比如Global.ascx.cs,Main方法等地方调用的。

但是通过代码调用设置数据库的初始化方式并不是很方便,因为每种初始化方式都应用于不同的场合,当我们从开发环境变化到production环境时,肯定会使用不同的初始化方式,比如说从DropCreateDatabaseIfModelChanges变为CreateDatabaseIfNotExists。如果每次变化都要重新改代码,重新编译的话,太不方便了。

我们可以通过配置指定数据库初始化的方式,这样就可以更灵活的改变我们的初始化方式:

<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="DatabaseInitializerForType OrderSystemContext"
value="System.Data.Entity.DropCreateDatabaseIfModelChanges[[OrderSystemContext]], EntityFramework" />

</appSettings>
</configuration>

我们还以自定义数据库初始化类,通过自定的初始化类,还可以将一些基础数据在创建数据库之后插入到数据库中去。

假设我们在测试环境中测试对Product类的相关操作,我们需要一些ProductCatalog的基础数据,因为Product中有一个Productcatalog的引用。我们可以定义一个自己的数据库初始化类,继承DropCreateDatabaseAlways,让Code First每次在执行测试之前都删除掉原来的数据库并且插入一些ProductCatalog的测试数据。

 

public class DropCreateOrderDatabaseWithSeedValueAlways : DropCreateDatabaseAlways<OrderSystemContext>
   {
       protected override void Seed(OrderSystemContext context)
       {
           context.ProductCatalogs.Add(new ProductCatalog { CatalogName = "DELL E6400", Manufactory = "DELL", ListPrice = 5600, NetPrice = 4300 });
           context.ProductCatalogs.Add(new ProductCatalog { CatalogName = "DELL E6410", Manufactory = "DELL", ListPrice = 6500, NetPrice = 5100 });
           context.ProductCatalogs.Add(new ProductCatalog { CatalogName = "DELL E6420", Manufactory = "DELL", ListPrice = 7000, NetPrice = 5400 });
       }
   }

 

我们在测试类的测试初始化方法中就可以指定Code First使用我们自定义的初始化类进行数据库的初始化:

Database.SetInitializer(new DropCreateOrderDatabaseWithSeedValueAlways());

然后我们就可以使用初始化时插入的基础数据进行我们的测试了:

[TestMethod]
        public void CanAddProduct()
        {
            OrderSystemContext unitOfWork = new OrderSystemContext();
            ProductRepository repository = new ProductRepository(unitOfWork);
            ProductCatalog catalog = repository.SearchProductCatalog(c => c.CatalogName == "DELL E6400", 1, 10)[0];
            Product product = new Product { Catalog = catalog, CreateDate = DateTime.Parse("2010-2-10"), ExpireDate = DateTime.Parse("2012-2-10") };
            repository.AddNewProduct(product);
            unitOfWork.CommitChanges();
        }

 

我们打开SQL Server就可以发现基础的测试数据了:

 

PS:接下来的日记将涉及DbContext的一些配置,有兴趣剧透的同学可以继续猜测一下,谜题明天揭晓。

本文链接


    
[2]企业级应用架构(NHibernater+Spring.Net+MVC3)_2.0
    来源:    发布时间: 2013-10-28

         本最小架构在1.0版本的基础上进行了进一步改进,适用于B/S结构的企业级系统应用,本系统依然站在巨人的肩膀上(马丁福勒),是在.NET领域中的实现模式的一种探索,供大家研究学习并应用于实际项目中。架构整体风格为三层架构,做到了松散耦合、可扩展性、易维护性等诸多有点。其较之前一个版本1.0而言已有较大改观和进步,主要体现在以下几点:

 

1、Spring深度整合MVC和Hibernate,MVC当“导演”,Spring当“管家”,Hibernate当“采购总监”,(*^__^*)

2、Spring升级到1.3.2,Hibernate升级到3.2,MVC依然为3.0

3、架构模块职责进一步划清,进一步提高了松耦合。

4、引入SpringAOP编程,解决DAO异常日志记录。

5、重新改善了Log4net组件的编程模式。

6、将对象依赖配置文件集中管理,一改过去分散管理的状态。

7、完善了Spring事物配置的通用性。

8、绝大部分对象之间依赖都使用Spring“依赖注入”,提高系统松耦合性,以及便利的实现了功能类的“单例性。

9、改Facade层为Segregate层。Facade层为远程调用,而使用Segregate(隔离)层在这里更加适用,并增加接口。

10、丰富样例,添加了编程规范注释。并且可以使用Ctrl+F5运行出样例。

代码:

EnterpriseArchitecture-2

运行环境:

VS2010、MVC3、.NetFramework4.0、Window XP以上、SQLServer NorthWind数据库

架构图:

其余设计和1.0版本类似,请参考1.0版本资料

                                                                     企业级分层架构视图

一、三层架构
      在福勒阐述的企业级应用常见的基于三层的架构模式中(Transaction Script、DomainModel、Table Module以及Active Record),建议我们无论项目大小统一采用DomainModel模式。这样有很多好处,统一架构风格,统一了思想和编程模式,为应用升级带来了极大的可扩展、易维护的空间,研究表明在使用“领域模型”模式的系统中对于大规模业务逻辑的系统具有极强的适应能力。
二、项目开发架构
      如下图所示,在.net环境中我们实现三层架构的类库结构分为如下几个模块类库。各个模块的功能职责定义如下:
                                             
    1.Web:这里是MVC应用,表示模块和具体的应用相关连,可以是ASP.NET、WCF或Console等应用,
        她应  该只关注于对客户的表示,不关心业务。MVC应用中Control职责就应该如此。
    2.Model:这个模块即为DTO(数据传输对象),它的作用主要是为表示层的视图提供数据封装。MVC应用 
       中这里就充当了Model模块的分离。
    3.Segregate:即为隔离层,隔离层的作用将是将表示层和业务层彻底分离,实现两个层可移植性,互不
        依赖。所以她在这里的基本职责是为两个层的数据做类型转化以及验证表示层数据有效性。
       很多人认为  因为MVC框架提供强大的验证数据有效性,所以数据验证应该在MVC的Model中验证,其实不然,因为既然是想做出高度可移植性的系统,必然系统的表示层将来可能会换成别的应用,一旦表示层换了,那么自然MVC的数据验证就不存在了,这个时候你就不得不在新的表示层项目中重写数据有效性验证了,悲剧!
       当然,如果你的应用永远不会改成别的表示层,那么使用MVC的Model验证方式确实能够减轻不少编程压力,而且代码简洁,并提供客户端和服务端一致的验证技术。
     4.Service:即为应用逻辑层,它是一个粗粒度的逻辑封装,几乎相当于系统的某个用例(用户的触发事件),相当于一个操作脚本。几乎可以写成一种公式:业务=应用逻辑+领域逻辑+数据逻辑。这个公式强调了这三大逻辑的分离理论,提高系统可移植性、松散耦合性。领域逻辑分离为Domain库,而数据逻辑分离为DAO库。同时这里也是事务运行区。
      5.Domain:即为领域模型,虽然大家很少会去建立领域模型,但我们的数据库中的实体实际上就是领域模型的一部分,而且十分相似,领域模型的对象拥有属性和方法。其方法就是我们指的“领域逻辑”。
      6.DAO:即为数据访问层,很多人理解为就是访问数据库的,其实它的职责还包括一切持久化数据的访问(文件、视频、图片等)。
         一个关于DAO的争论是,他应该是什么样的,有人趋向于“通用查找”,而有人倾向于具体“数据逻辑”方法,这样的结果是前者的Dao中代码是固定的几个匹配方法,而后者Dao就成了一个Repository(资源库),其中包含了大量的各种数据查找逻辑,因为它完全把操作数据的逻辑从应用逻辑中分离出来了,这两种应用模式都有人使用。
         但我更倾向于后者,因为这样也有许多好处,其一,就是它将数据逻辑分离出来,这样以后系统更换了数据库或数据访问组件,我们只需要更改一下Dao方法中的实现即可,而无需改动系统其它部分,提高了系统可移植性和松散耦合性,Very Good!其次,就是减轻了Service的负担,在一个简单的增删改查系统中“数据逻辑”耦合在“应用逻辑”中还好,不会使代码过长,但在长流程的业务系统中,分离出“数据逻辑”无疑是明智之举。
         再来说说前者的情况,前者就是想把“数据逻辑”和“应用逻辑”一起放到Service中,这样在他们看来的好处是,所有的Dao方法是固定的,构造取数据的逻辑由Service完成,Service只传递一个查询条件给Dao即可。但这也会有一些不足,其一,就是在长流程的业务系统中Service忙着实现流程操作,而此时再给她增加构造数据查询条件的代码,这样会使得其代码变得冗长,也不容易阅读。其次,为了实现业务层和数据层分离,我们是不会在Service中写耦合第三方数据访问组件的API的(如:Hibernate),这样就会促使你不得不自己实现一套数据查询条件构造类,而如HQL语句却不能使用。
        基于“分离关注点”的原则和这样那样的理由,我觉得使用“数据逻辑”分离的模式比较理想。     

本文链接


    
[3]分享懒人张RDLC报表(五、六)
    来源:    发布时间: 2013-10-28

来自蜡人张:RDLC报表(五)

        随着Visual Studio 2005中文版的推出,Microsoft汉化了MSDN的大部分内容,开发者再也不用啃英文了,本来想介绍一下LocalReport的Render方法,现在您可以到http://msdn2.microsoft.com/zh-cn/library/ms252207(VS.80).aspx获 得关于这部分的详细信息。之所以以前想介绍这个方法,是因为我将想大家介绍一种在Crystal Report中无法实现的自定义票据打印纸张的方法。Anyway,现在我直接向大家介绍这种方法,可能这种方法并不是很好的,但是确实是我经过一段时间 的摸索总结出来的。萝卜(http://luobos.cnblogs.com)曾经提到过的变通的方法不知道是不是我要介绍的这一种,欢迎和我进行交流!

        要想使用RDLC报表并进行页面设置,我们先来看一下LocalReport是否有类似PageSettings的类、属性、方法或事件等,我仔细找了一 下,发现Microsoft.Reporting.WinForms.ReportPageSettings类具有PaperSize属性和Margin 属性,但可惜的是它们都是只读的,对我们来说没有意义;另外,LocalReport具有GetDefaultPageSettings()方法,这也只 能是获取当前报表的页面设置。没办法,只能采用变通的方法了。在.NET中如果想使用自定义纸张,最好的方法莫过于使用 System.Drawing.Printing.PrintDocument类了,还记得我在前面提到的一个GotReportViewer的例子吗?

1 private int m_currentPageIndex;
2 private IList<Stream> m_streams;
3
4 private Stream CreateStream(string name, string fileNameExtension, Encoding encoding, string mimeType, bool willSeek)
5 {
6 Stream stream = new FileStream(name + "." + fileNameExtension, FileMode.Create);
7 m_streams.Add(stream);
8 return stream;
9 }
10
11 private void Export(LocalReport report)
12 {
13 string deviceInfo =
14 "<DeviceInfo>" +
15 " <OutputFormat>EMF</OutputFormat>" +
16 " <PageWidth>8.5in</PageWidth>" +
17 " <PageHeight>11in</PageHeight>" +
18 " <MarginTop>0.25in</MarginTop>" +
19 " <MarginLeft>0.25in</MarginLeft>" +
20 " <MarginRight>0.25in</MarginRight>" +
21 " <MarginBottom>0.25in</MarginBottom>" +
22 "</DeviceInfo>";
23 Warning[] warnings;
24 m_streams = new List<Stream>();
25 report.Render("Image", deviceInfo, CreateStream, out warnings);
26
27 foreach (Stream stream in m_streams)
28 stream.Position = 0;
29 }
30
31 private void PrintPage(object sender, PrintPageEventArgs ev)
32 {
33 Metafile pageImage = new Metafile(m_streams[m_currentPageIndex]);
34 ev.Graphics.DrawImage(pageImage, ev.PageBounds);
35
36 m_currentPageIndex++;
37 ev.HasMorePages = (m_currentPageIndex < m_streams.Count);
38 }
39
40 private void Print()
41 {
42 const string printerName = "Microsoft Office Document Image Writer";
43
44 if (m_streams == null || m_streams.Count == 0)
45 return;
46
47 PrintDocument printDoc = new PrintDocument();
48 printDoc.PrinterSettings.PrinterName = printerName;
49 if (!printDoc.PrinterSettings.IsValid)
50 {

    
最新技术文章:
 




特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

©2012-2021,,E-mail:www_#163.com(请将#改为@)

浙ICP备11055608号-3