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); } }
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.
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.
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.
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");
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With