俄罗斯贵宾会-俄罗斯贵宾会官网
做最好的网站

[C#]关于DataDirectory的一些思考

笔者在使用Entity Framework中的Scaffolding机制自动创建拓展名为mdf的数据库及表单时,遇到如下的错误:

在项目中用程序中嵌入mdf文件的方式来进行SQLServer数据库开发非常方便,用来发布开源项目等很方便,点击就可以运行,免部署,特别是在教学中 用起来更加方便,老师不用先将数据库文件detach再发给学生,学生也不用将数据库文件attach。采用项目中嵌入mdf文件的方式,老师把讲课的代 码发给学生,学生打开就可以运行。我在传智播客.net培训班教学中就是用的这种方式进行讲解。

A file activation error occurred. 
The physical file name '\MusicDBContext.mdf' may be incorrect. 
Diagnose and correct additional errors, and retry the operation.
CREATE DATABASE failed. Some file names listed could not be created. 
Check related errors.

在ASP.net程序中只要将mdf文件放到项目的App_Data文件夹即可,在连接字符串中使用
Data Source=.SQLEXPRESS;AttachDbFilename=|DataDirectory|俄罗斯贵宾会,CallCenter.mdf;Integrated Security=True;User Instance=True
做连接字符串即可。


但是在WinForm程序中,如果在项目的App_Data文件夹中新建一个mdf文件,然后用
Data Source=.SQLEXPRESS;AttachDbFilename=|DataDirectory|CallCenter.mdf;Integrated Security=True;User Instance=True
进行连接会提示找不到CallCenter.mdf。原来WinForm程序并不会去App_Data中找mdf文件。原来在ASP.net中 DataDirectory的值是当前项目的App_Data路径,而WinForm中的DataDirectory值则是当前项目的路径,因此 Winform中mdf文件不用放到App_Data中,放到项目根目录下就可以。

 

但是新问题随之又来了,在WinForm中用这种方式开发的时候有时候改了项目中mdf文件中的表中的数据或者表结构,运行的时候却发现运行时通过程序读 取的数据或者表结构没有变,而有时调试时Insert插入的数据在这次调试的时候竟然没有了。经过研究发现,WinForm程序运行的时候连接的是 bin/Debug下的mdf文件,而不是项目中的mdf文件,这是和ASP.net程序行为不同的地方。每次程序发生Build行为的时候,项目中的 mdf就会覆盖bing/Debug下的mdf文件,也就是有两个mdf文件的存在,项目中的mdf相当于“源文件”。虽然可以通过修改文件的 “BuildToOuput”属性来部分解决问题,但是仍然不是很完美。

首先回顾一下创建这个程序的步骤:

有一个比较很直接的想法,就是让程序去连接项目中的mdf文件,而不是连接bin/Debug下那个。
经过查询资料找到了修改方法,在Program.cs文件Main函数最开始加入如下代码:
string dataDir = AppDomain.CurrentDomain.BaseDirectory;
            if (dataDir.EndsWith(@"binDebug")
                || dataDir.EndsWith(@"binRelease"))
            {
                dataDir = System.IO.Directory.GetParent(dataDir).Parent.Parent.FullName;
                AppDomain.CurrentDomain.SetData("DataDirectory", dataDir);
            }

1、创建一个Console控制台应用程序,程序集名称及命名空间为ConsoleApp;

原理简单分析:连接字符串中的DataDirectory的值就是通过AppDomain.CurrentDomain.SetData赋值过去的,如果 当前程序的目录以"binDebug"或者"binRelease"则认为它是运行在VisualStudio环境中,就取项目的目录然后赋 值给DataDirectory这个key。既然是CurrentDomain.SetData,估计对于非默认AppDomain中的数据库连接代码可 能会不起作用(只是猜测,没验证),这就要需要创建子AppDomain的时候再去赋值了。

2、使用程序包控制台管理器将Entity Framework包含到此程序中,代码如下:

上面的代码还是有一点潜在的bug的,比如正式的运行的时候exe被很杯具的放到了某个binDebug目录下,就会有问题,不过想想正式生产环境运行 的时候肯定不会用这种AttachDbFilename方式,这种方式只存在于开发环境,因此也就睁一只眼闭一只眼了,呵呵。

PM> install-package Entity Framework

[引用来自 www.rupeng.com/forum/thread-11988-1-1.html]

3、在App.Config文件中将以下内容插入到configuration节点:

<connectionStrings>
    <add name="MusicDBContext"
       connectionString="Data Source=(LocalDb)MSSQLLocalDB;
         Initial Catalog=MusicDBContext;Integrated Security=SSPI;
         AttachDBFilename=|DataDirectory|MusicDBContext.mdf"
       providerName="System.Data.SqlClient" />
</connectionStrings> 

4、在控制台编写以下代码:

using System;
using System.Linq;
using System.Data.Entity;
namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                MusicDbContext db = new MusicDbContext();
                Music music = new Music { Title = "Far Away From Home", 
                                          ReleaseDate = DateTime.Now };
                db.Musics.Add(music);
                db.SaveChanges();
                db.Musics.ToList().ForEach(x => Console.WriteLine($"{x.ID},
                                                {x.Title},{x.ReleaseDate}"));
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                if(ex.InnerException != null)
                {
                    Console.WriteLine(ex.InnerException.Message);
                }
            }
            Console.ReadKey();
        }
    }
    public class Music
    {
        public int ID { get; set; }
        public string Title { get; set; }
        public DateTime ReleaseDate { set; get; }
    }
    public class MusicDbContext : DbContext
    {
        public MusicDbContext() : base("MusicDBContext") { }
        public DbSet<Music> Musics { set; get; }
    }
}

5、运行此程序,发现程序不能按自己想要的结果运行,出现在最前面出现的错误。


通过查看出错的信息,发现

AttachDBFilename=|DataDirectory|MusicDBContext.mdf

有问题,而这又是没有问题的,这到底是怎么回事?为什么会出现错误?

于是,通过MSDN查找相关资料,通过以下方法获得DataDirectory指定的路径是什么:

object path = AppDomain.CurrentDomain.GetData("DataDirectory");

运行此行代码,发现path居然是null!!!什么?一般控制台或者Windows Form程序根据是Debug还是Release决定DataDirectory的初始化路径为Bebug文件夹还是Release文件夹吗?

这个错了。

如果原先的Bebug文件夹或Release文件夹存在数据库文件,使用类似"AttachDBFilename=|DataDirectory|MusicDBContext.mdf"的写法是没有问题的,

即使path = null,它也知道是在Bebug文件夹或Release文件夹下。

本文由俄罗斯贵宾会发布于编程,转载请注明出处:[C#]关于DataDirectory的一些思考

您可能还会对下面的文章感兴趣: