After days of samples, demos, tutorials, walkthroughs, etc., ...
I created the code below based on what I've learned.
It works perfect when hard coded with an entity (as shown in the top commented 2 lines for the Advertisers db table).
It creates the excel file, then it offers it up for download to the user via the browser.
However, I wanted to make it a general purpose utility for creating and downloading other entities in my db.
I've refactored this from my original method:
by replacing all the 'Advertisers' references with the passed in parameter 'listTitle'.
Then, I replaced the var adv with the passed in parameter 'context' in var ctx.
[Updated]
I plan to call this method with a collection list I create from other areas of my code (similar to the way I used the var adv, commented (i.e., context = db.Publisher.OrderBy(p => p.Name).ToList())),
and pass it in as the context parameter in the method call.
(ex., ExportExcelList("Publishers", context)).
Now, I get the red squigglies on the line with LoadFromCollection, and it won't build my code properly.
So my questions are:
How can I get this to work?
Is there a way of getting this to work without hugh blocks of code that include annonymous object types and GUID's (as described in other posts)?
Am I just missing a using statement?
Or should the type of the context parameter be different?
Currently, for the EPPlus using statement, I'm using: using OfficeOpenXml;
I've installed EPPlus via Nuget.
This is my code:
[HttpPost]
public ActionResult ExportExcelList(string listTitle, ApplicationDbContext context)
{
////create the list of advertisers [ HARD CODED HERE ]
//var adv = db.Advertisers.OrderBy(a => a.Name).ToList();
var ctx = context;
//create the path and filename to use for the excel file and check for file exists/delete here.
//here xfile is the name of the excel file without the directory path.
....
file = new FileInfo(filename); //this is the file object used for creating the new excel file.
//create the excel file
using (ExcelPackage ep = new ExcelPackage(file))
{
ep.Workbook.Worksheets.Add(listTitle);
var worksheet = ep.Workbook.Worksheets[1];
worksheet.Cells[1, 1].LoadFromCollection(ctx, true); //this line barks at the ctx usage, it wants explicit type declaration.
ep.Save();
}
//download the excel file to the user
using (ExcelPackage ep = new ExcelPackage(file))
{
//Write it back to the client
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("content-disposition", "attachment; filename=" + xfile);
Response.BinaryWrite(ep.GetAsByteArray());
}
//end this process
return null;
}
I haven't found a solution that would allow me to do what I wanted.
Many thanks to #CodingYoshi for his insights.
Below is my temporary solution. For now I'm using a switch case scenario.
Not too happy about it, but it serves the purpose for now, in spite of it's limitations.
Hopefully, someone here at SO can treat to a better solution. I hope.
[ Updated Code ]
//since I can't pass the context in by parameter when calling this method, I've decided to use a switch case scenario and just pass in the listTitle
switch(listTitle)
{
case "Advertisers":
worksheet.Cells[1, 1].LoadFromCollection(
db.Advertisers.OrderBy(a => a.Name).ToList(),
true);
break;
case "Publishers":
worksheet.Cells[1, 1].LoadFromCollection(
db.Publishers.OrderBy(a => a.Name).ToList(),
true);
break;
}
ep.Save();
That line breaks because it is expecting IEnumerable but you are passing an ApplicationDbContext. If you want to do what you are after, you need to somehow figure out a way to enumerate all the dbsets in your ApplicationDbContext and pass them one by one to the LoadFromCollection method.
I know DbContext has a property which will allow you to do this: GetDbSetProperties, not sure if ApplicationDbContext has something similar. That will get you all your tables into Excel.
The next thing you will need to worry about is the filters and sort code. For example, maybe you want to sort by a specific field (by Name for advertisers as you have) or maybe you want to filter them. Normally this will be passed in using a predicate but since your are hitting the action method from the browser, that is not an option. Therefore, you can create multiple meaningful actions such as ExportAdvertisersSortedByName, ExportTopAdvertisers, ExportSomeOtherEntity etc. For these actions you will hardcode the entity like you have above and then the common code for epplus export can be in another method.
This way you can export a whole database, or specific items for specific needs.
You need to use reflection to pass the collection name dynamically.
So like this
var p = ctx.GetType.GetProperty(listTitle).getValue(ctx, null)
and see further details here.
public static IList<T> SelectAll<T>() where T : class
{
try
{
using (NWindDataContext context = new NWindDataContext())
{
// get the table matching the type
// and return all of it as typed list
var table = context.GetTable<T>().ToList<T>();
return table;
}
}
catch (Exception)
{
throw;
}
}
Related
I need to get the Application Title from an access database via C#. I have seen some samples using Office VBA: I.E: https://learn.microsoft.com/en-us/office/vba/api/access.application.apptitle
The issue is that I don't have a reference to a class library to be able to access the Application.AppTitle property. I have tried a few references, most notably:
using Microsoft.Office.Interop.Access.Dao
But I can't access the Application.AppTitle via any of the properties. For example:
var dbe = new Microsoft.Office.Interop.Access.Dao.DBEngine();
var db = dbe.OpenDatabase(#"c:\\Sample.mdb");
// Show database properties
// db.Properties. "When I expand this there is no AppTitle"
Does anyone have any other approach that has worked for them to access the MS Access AppTitle via C#?
Thanks in advance!
Ian
The AppTitle property doesn't show up until you set it in Access or via code (and with RefreshTitleBar
Your code works because you're looping to check for the name. You won't find the property if it's empty.
You can use the loop method like you do above or check for it directly using database.properties("AppTitle") - just make sure you trap for an error in case it's empty
Update: I figured it out. This was different than I expected, but the solution was:
var dbEngine = new DBEngine();
var database = dbEngine.OpenDatabase(#"c:\\Sample.mdb");
Microsoft.Office.Interop.Access.Dao.Property allowBypassKeyProperty = null;
foreach (Microsoft.Office.Interop.Access.Dao.Property property in database.Properties)
{
if (property != null)
{
if (property.Name == "AppTitle")
{
MessageBox.Show(property.Name.ToString());
MessageBox.Show(property.Value.ToString());
}
}
}
I am writing a Rider/ReSharper nav-from-here plugin which is supposed to determine a target type based on a symbol I am standing on using some simple rules, and finally, navigate to it.
The first part is okay, I have managed to form the FQN needed, but I am struggling with the navigation. I found this StackOverflow post, and thought I might try this approach. So I have been trying to use TypeFactory.CreateTypeByCLRName for like two hours to create an IDeclaredType instance to be able to get the IDeclaredElement using GetTypeElement() and eventually get its declarations. But the API seems to have changed and no matter what I do I cannot get my code to work.
Here is what I've got so far:
// does not work with Modules.GetModules(), either
foreach (var psiModule in solution.GetPsiServices().Modules.GetSourceModules())
{
var type = TypeFactory.CreateTypeByCLRName("MyNamespace.MyClassName", psiModule);
var typeElement = type.GetTypeElement();
if (typeElement != null)
{
MessageBox.ShowInfo(psiModule.Name); // to make sure sth is happening
break;
}
}
The weird part is, I actually see a message box - but only when the tab with MyClassName.cs is active. When it is in focus, everything is fine. When it's not or the file is closed, the class does not get resolved, type.IsResolved is false.
What am I doing wrong?
To do this, you should have a IPsiModule instance from the context where you plan to use the type you're looking for. You can get it from some syntax node you're working with via .GetPsiModule() method or via many other ways (like dataContext.GetData(PsiDataConstants.SOURCE_FILE)?.GetPsiModule().
void FindTypes(string fullTypeName, IPsiModule psiModule)
{
// access the symbol cache where all the solution types are stored
var symbolCache = psiModule.GetPsiServices().Symbols;
// get a view on that cache from specific IPsiModule, include all referenced assemblies
var symbolScope = symbolCache.GetSymbolScope(psiModule, withReferences: true, caseSensitive: true);
// or use this to search through all of the solution types
// var symbolScope = symbolCache.GetSymbolScope(LibrarySymbolScope.FULL, caseSensitive: true);
// request all the type symbols with the specified full type name
foreach (var typeElement in symbolScope.GetTypeElementsByCLRName(fullTypeName))
{
// ...
}
}
I Have spent hours looking but without any success.
I am programmatically creating snap shots of reports in SSRS using c# which creates report folders. The reports are created in these folders but to prevent errors occurring I am deleting the whole folder structure and then re-creating the reports to prevent SSRS throwing an exception.
I am using ReportingService2010.
ReportingService2010.DeleteItem(deleteFolderPath);
…
ReportingService2010.CreateFolder(folder, parentFolder, null);
-- This is the line where I need to check if the folder and report exist
var ret = CheckExist(linkedReportName, newParent);
var param = GetReportParameters(existingReportPath);
ReportingService2010.SetItemParameters(existingReportPath, param);
-- If I do not delete the folder structure the error will be thrown after this in a try/Catch
ReportingService2010.CreateLinkedItem(linkedReportName, newParent, existingReportPath, props);
I need to add a method to see if the report and report folder has already been created
I think the nicest way is ReportService2010.GetItemType() method. This returns a string (ReportingService2010.ListItemTypes() method), including possible values "Unknown" for non-existent (rather than throwing an exception) or "Folder" for a folder.
Another possible alternative is to directly work with the path, type, and parentid fields of the Catalog table in your report server database. If you open SQL Server and look at the table, it should be pretty clear what you need to do.
You can use C# to run a SQL command such as
SELECT COUNT(*) FROM CATALOG WHERE PATH LIKE '%TheFolderNameImLookingFor%'
If the count is greater than zero, then the folder exists.
You can vary this for your needs. Folder items have a type of 1; Report items have a type of 2, so you can use this to distinguish between reports and folders.
How to delete folders if they exist using ListChildren;
var items = ReportingService2010.ListChildren(parentFolder, false);
if (items.Where(x => x.TypeName == "Folder").Any(x => x.Name == folder))
{
ReportingService2010.DeleteItem(folder, parentFolder, null);
}
Directory.Exists() might work for you.
if (Directory.Exists(folderString))
{
// Do something
}
Don't forget the System.IO namespace:
using System.IO;
UPDATE: In the future, if you're looking for a file, you can do the same thing with File.Exists(), which is also in the System.IO namespace.
if (!Directory.Exists(folder))
{
ReportingService2010.CreateFolder(folder, parentFolder, null);
}
I am new to JINT, and trying to just do some basic tests to kind of learn the ropes. My first attempt was to just store some javascript in my database, load it, and execute it in a unit test. So that looks essentially like this....
[Fact]
public void can_use_jint_engine() {
using (var database = DocumentStore()) {
using (var session = database.OpenSession()) {
var source = session.Load<Statistic>("statistics/1");
// join the list of strings into a single script
var script = String.Join("\n", source.Scripting);
// this will create the script
// console.log("this is a test from jint.");
//
var engine = new Jint.Engine();
// attempt to execute the script
engine.Execute(script);
}
}
}
And it doesn't work, I get this error, which makes absolutely no sense to me, and I cannot find any documentation on.
Jint.Runtime.JavaScriptExceptionconsole is not defined at
Jint.Engine.Execute(Program program) at
Jint.Engine.Execute(String source) at
SampleProject.Installers.Instanced.__testing_installer.can_use_jint_engine()
in _testing_installer.cs: line 318
Can anyone assist in shedding some light on this? I'm pretty confused at this point.
With JavaScript there are three entities - we care about. The host (browser, your application etc), the engine (JINT in this case) and the script ("console.log(...)") in this case.
JavaScript defines a bunch of functions and object as part of the language, but console is not one of them. By convention, browsers define a console object that can be used in the manner you describe. However, since your app is not a browser (and JINT does not do this by itself), there's no console object defined in your namespace (globals).
What you need to do is add a console object that will be accessible in JINT. You can find how to do this in the docs, but here's a simple example of how to add a log function to the engine so it can be used from the JS code (example taken from github).
var engine = new Engine()
.SetValue("log", new Action<object>(Console.WriteLine))
;
engine.Execute(#"
function hello() {
log('Hello World');
};
hello();
");
I'm trying to standardise some data access code with my colleagues. One of the aforementioned colleagues asserts that the EntLib Data Access Block trys to cache parameters on stored proc calls.
I've had a look in reflector and there is some evidence that it could be caching them. But I don't think it does in the following situation.
public Dictionary<long, string> GetQueue(int maxItems)
{
var sq = new SqlDatabase(_connString.ConnectionString);
var result = new Dictionary<long, string>();
using (var cmd = (SqlCommand)sq.GetStoredProcCommand("dbo.GetQueue"))
{
sq.AddInParameter(cmd, "maxItems", DbType.Int32, maxItems);
var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
while (reader.Read())
{
long id = reader.GetInt64(reader.GetOrdinal("id"));
string fileName = reader.GetString(reader.GetOrdinal("meta_data_filename"));
result.Add(id, fileName);
}
}
return result;
}
Can anyone confirm or deny this?
I'm using EntLib 4.1
It definetly used to, I ripped the code out and threw in in my library.
it used sp_help and parsed the output to determine the data types.
These days, I ripped the code out, .Net is much much better about adding parameters.
cmd.Parameters.AddWithValue("#name",somevalue)
in your example of you keep reflectoring ... you will find it being done down this path GetStoredProcCommand()
You will get a Command object back, already populated with parameters
The ent lib code is copyrighted, but the code is almost identical to this
http://code.google.com/p/dbdotnet/source/browse/trunk/ParameterCache.cs
As far as I can tell it doesn't cache the parameters. Using the same instance of a Database object I called DiscoverParameters multiple times while running a trace. Each time I call DiscoverParameters I can see a [sys].[sp_procedure_params_100_managed] so it looks like it's making the round trip every time.
Here's an example of how to do it yourself that's seems like it might be alright:
http://davidhayden.com/blog/dave/archive/2006/11/03/CachingStoredProcedureParameters.aspx