Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to read a resource file within a Portable Class Library?

Tags:

I have a Portable Library which I am using for a Windows Phone application. In that same Portable Library, I have a couple of content files (Build Action = Content).

I created a class DataReader in the Portable Library which is supposed to return me a stream to the content file. However, with the code below I am consistently getting back null from GetManifestResourceStream. What am I doing wrong?

public class DataReader {     public static Stream GetStream(string code)     {         string path = string.Format("./data/code-{0}.dat", code);         return Assembly.GetExecutingAssembly().GetManifestResourceStream(path);     } } 
like image 442
Martin Avatar asked Jun 09 '12 19:06

Martin


People also ask

What is portable class library?

The Portable Class Library project enables you to write and build managed assemblies that work on more than one . NET Framework platform. You can create classes that contain code you wish to share across many projects, such as shared business logic, and then reference those classes from different types of projects.

What is a PCL project?

PCL projects target specific profiles that support a known set of BCL classes/features. However, the down side to PCL is that they often require extra architectural effort to separate profile specific code into their own libraries.

What is .NET PCL?

PCL: libraries that target a set of platform (have the 'least common denominator' of APIs of those platforms). So when writing a PCL you're saying I want to target platform A, B, C.


2 Answers

Your path is wrong. You're using slashes, but in the embedded manifest resource names slashes were converted to periods during the build. Also depending on your PCL targeted platforms, you may not even be able to call Assembly.GetExecutingAssembly().

Here is what you can do:

var assembly = typeof(AnyTypeInYourAssembly).GetTypeInfo().Assembly;  // Use this help aid to figure out what the actual manifest resource name is. string[] resources = assembly.GetManifestResourceNames();  // Once you figure out the name, pass it in as the argument here. Stream stream = assembly.GetManifestResourceStream("Some.Path.AndFileName.Ext"); 
like image 147
Andrew Arnott Avatar answered Oct 11 '22 08:10

Andrew Arnott


From http://social.msdn.microsoft.com/Forums/windowsapps/en-US/386eb3b2-e98e-4bbc-985f-fc143db6ee36/read-local-file-in-portable-library#386eb3b2-e98e-4bbc-985f-fc143db6ee36

File access cannot be done portably between Windows Store apps and Windows Phone 8 apps. You will have to use platform specific code, to open the file and acquire a stream. You can then pass the stream into the PCL.

If you build it with the Content build action, the XML is not inside of the DLL. It's on the filesystem, and there's no way to get it from inside of the PCL. That is why all of the answers set the build action to Embedded Resource. It places the file inside MyPCL.DLL\Path\To\Content.xml.

However, if you set the build action to Content and set the copy type to Copy if newer, it will place your files in the same directory as the executable.

Solution Explorer, Properties, and Windows Explorer

Therefore, we can just place an interface for reading the file in our PCL. On startup of our nonportable code, we inject an implementation into the PCL.

namespace TestPCLContent {     public interface IContentProvider     {         string LoadContent(string relativePath);     } }  namespace TestPCLContent {     public class TestPCLContent     {         private IContentProvider _ContentProvider;         public IContentProvider ContentProvider         {             get             {                 return _ContentProvider;             }             set             {                 _ContentProvider = value;             }         }          public string GetContent()         {             return _ContentProvider.LoadContent(@"Content\buildcontent.xml");         }     } } 

Now that the PCL is defined above, we can create our interface implementation in nonportable code (below):

namespace WPFBuildContentTest {     class ContentProviderImplementation : IContentProvider     {         private static Assembly _CurrentAssembly;          private Assembly CurrentAssembly         {             get             {                 if (_CurrentAssembly == null)                 {                     _CurrentAssembly = System.Reflection.Assembly.GetExecutingAssembly();                 }                  return _CurrentAssembly;             }         }          public string LoadContent(string relativePath)         {             string localXMLUrl = Path.Combine(Path.GetDirectoryName(CurrentAssembly.GetName().CodeBase), relativePath);             return File.ReadAllText(new Uri(localXMLUrl).LocalPath);         }     } } 

On application startup, we inject the implementation, and demonstrate loading contents.

namespace WPFBuildContentTest {     //App entrance point. In this case, a WPF Window     public partial class MainWindow : Window     {         public MainWindow()         {             InitializeComponent();         }          private void Window_Loaded(object sender, RoutedEventArgs e)         {             ContentProviderImplementation cpi = new ContentProviderImplementation();              TestPCLContent.TestPCLContent tpc = new TestPCLContent.TestPCLContent();             tpc.ContentProvider = cpi; //injection              string content = tpc.GetContent(); //loading         }     } } 

EDIT: I kept it strings instead of Streams for simplicity.

like image 28
Millie Smith Avatar answered Oct 11 '22 08:10

Millie Smith