EF实体,本地文件作为属性

时间:2013-11-18 04:39:58

标签: c# asp.net entity-framework

我有一个实体,它既有存储在数据库表中的常规属性,也有对系统磁盘上本地文件的引用。我希望将此文件的create / replace / delete方法封装在数据访问层中,让应用程序的其他部分不关心应该如何以及在何处存储它,只需发送一个字节流perform" clear&# 34;操作。与此同时,我喜欢在web.config中定义的文件目录,如数据库访问参数 我是我的网络应用程序我使用EF 5 Code First并定义了实体,如下例所示:

// EF entity
public class UserImage{
    public string Description { get; set; } 
    [NotMapped]
    public LocalFile File { get; set; } 
}

// not EF entity
public class LocalFile{
    public string Name { get; set; } 
    public string LocalPath { // should return full path as dir path + file name } 
    public string Url { // should return LocalPath mapped to Url } 
    public void Set(FileStream fs) { // saves file to disk } 
    public void Clear() { // deletes file } 
}

在我的方法中,我可以说我的DbContext不仅是数据库上下文,而且是数据库和文件系统存储的上下文,我可以在创建时提供数据库连接字符串和本地目录路径。我认为这应该是一个很好的做法,如果我错了,请告诉我 现在问题是:如何从LocalFile或UserImage对象内部了解本地目录路径,以便它们可以实现LocalPath和Url属性?换句话说,应用程序的其他部分如何知道LocalFile或UserImage实例的实际Path / Url是什么?或者有没有办法为这些对象提供LocalDir,因为它们是在DbContext中创建的?最后,在UserImage中封装本地存储操作的替代方法是什么,这样任何代码都不关心文件的存储方式和位置?

2 个答案:

答案 0 :(得分:1)

您应该为文件操作创建接口,该接口将包含两个方法:Stream GetFile(string fileName)void PutFile(Stream fileStream, string fileName),并使用具有参数locationPath的构造函数的具体类来实现它:

public interface IFileRepository
{
    Stream GetFile(string fileName);
    void PutFile(Stream fileStream, string fileName);
}

public class FileRepository
{
    private readonly string localPath;
    public FileRepository(string localPath)
    {
        _localPath = localPath;
    }
    public Stream GetFile(string fileName)
    {
        var file = //get filestram from harddrive using fileName and localPath
        return file;
    }

    ...
    public void PutFile(Stream fileStream, string fileName)
    {
        //save input stream to disk file using fileName and localPath
    }
}

在DbContext类中,您应该创建IFileRepository类型的私有字段,并在构造函数中从参数初始化它:

public class SomeDbContext:DbContext
{
    private readonly IFileRepository _fileRepository;
    public SomeDbContext(IFileRepository fileRepository)
    {
        _fileRepository = fileRepository;
    }
    ...
}

并使用此_fileRepository在DbContext方法中放置和获取文件。

接口类型参数的具体类应该由Inversion of Control容器(或其他实现的Inversion of Control原理)传递。

<强>更新

public class UserImage
{
    public string Description { get; set; } 
    [NotMapped]
    public LocalFile File { get; set; } 
}

// not EF entity
public class LocalFile
{
    private readonly string _filePath;
    public LocalFile(string filePath)
    {
        _filePath=filePath;
    }    
    public string Name { get; set; } 
    public string LocalPath { // aggregate Name and filePath } 
    public string Url { // should return LocalPath mapped to Url } If i where you, i would write helper for this
}

答案 1 :(得分:0)

我认为我的错误是我正在尝试从实体内部访问上下文属性(即目录路径)。 EF数据库上下文体系结构没有实现它,实体也不知道它们是如何存储的。我不想发布这个经典 用于存储文件的目录可以被视为上下文属性(如连接字符串)和实体属性(如路径)。为了实现第一种情况,我可以为myDbContext提供Directory属性,然后通过调用myContext.GetUrl(userImage.FileName)通过上下文实例解析所有路径。但myDbContext并非始终可以从演示文稿级别直接访问,我将无法提取userImage的{​​{1}}将其设置在网页上,直到我向所有人宣传Url上层。
如果我将myDbContext视为Directory的属性,那么我需要以某种方式在构造函数中注入其值:

LocalFile

或使用之前设置的静态目录(比如在global.asax中):

public class LocalFile{
    // Constructor
    public LocalFile(string dir){ // set current dir }
    private string _dir;
    public GetUrl(){ // return _dir + filename }
}
// cons: parameterless constructor that will be called by DbContext on getting 
// data from DB won't set `_dir` and GetUrl won't return correct result

甚至直接访问web.config以获取路径:

public class LocalFile{
    // Constructor
    public LocalFile(){ // empty }
    public static Dir;
    public GetUrl(){ // return Dir + filename }
}

或在可访问web.config的上层创建扩展方法:

public class LocalFile{
    // Constructor
    public LocalFileType(){ // empty }
    public GetUrl(){ // read dir from web.config + filename }
}
// not good idea to access web-specific assemblies from EF data classes

因此,有许多可能的解决方案,每个解决方案都有其自身的缺点。我的情况有点复杂,因为我的目录路径还取决于public static class MyExtensions { public static string GetUrl(LocalFile localFile) { // dir from web.config + localFile.Name } } 的父用户ID,所以我在LocalFile而不是简单路径中有dir模板users/{0}/image.jpg。 我为实现目标所做的工作:

  • 放置web.config类型的url模板(0 - 父UserId,1 - fileName)到web.config     
  • 在我的EF实体附近创建了类设置

    users/{0}/{1}
  • 在应用程序启动时填充其值

    public static class Shared
    {
        public static Func<string, int, string> LocalFileUrlResolver;
    }
    
  • 让我的用户在创建时使用Url解析器提供自己的图像 时间

    protected void Application_Start()
    {
        Shared.LocalFileUrlResolver = 
            (fileName, userId) =>
                String.Format(ConfigurationManager.AppSettings["LocalFileUrl"], userId, fileName);
    }
    
  • 使我的LocalFile构造函数接受public User() { // ... Image = new LocalFile( "userpic.css", fileName => Shared.LocalFileUrlResolver(fileName, userId) ); } param 它解析了给定文件名的完整Url

    Func<string, string>

是的,这么多行。我从public class LocalFile { public LocalFile( string fileName, Func<string, string> fnUrlResolver ) { FileName = fileName; _fnUrlResolver = fnUrlResolver; } private readonly Func<string, string> _fnUrlResolver; public string FileName { get; private set; } public string Url { get { return _fnUrlResolver(FileName); } } } 获取dir模板,将其注入数据访问层的静态成员,并在web.config创建点上为用户的本地图像更具体。
我绝对不确定它是否值得花费。也许将来我会选择直接访问User:)