Lightswitch (Desktop app, out-of-browser) has very limited documentation scattered here and there. I'm looking for a way to clear all data in the intrinsic database in order to add new data after significant changes were made.
Here's the only working solution I have for now:
Write a DeleteAll() method for each and every VisualCollection I have.
Add an event or button to a screen, for example.
Call all the DeleteAll() methods (at event fired or button click).
Save.
This is obviously not efficient at all and very not DRY. What I'd like to have is some kind of ClearDatabase() method that I'd be using only for development and debugging.
So here are the 2 important parts of my question:
Can I (and if so, how would I) get all EntitySets in my ApplicationData without hardcoding ?
Is it possible to call such a method from the Client side of my app ? I'm thinking maybe in the auto-generated Application.Application_Initialize().
Since at the time of this post there seems to be absolutely no answer to this question on the internet, I came up with new code by digging in Lightswitch's code.
Here's a working, tested solution I wrote. Just follow those very simple steps.
In the solution explorer, under yourAppName.Server, create a new folder named UserCode, if it doesn't exist already.
In that folder, add a new class named DataUtilities.
Delete all code in that new class, and paste in this code:
using Microsoft.LightSwitch;
using Microsoft.LightSwitch.Details;
using Microsoft.LightSwitch.Framework;
using Microsoft.LightSwitch.Threading;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
namespace LightSwitchApplication.UserCode
{
public static class DataUtilities
{
public static void DeleteAllSets(this DataWorkspace workspace, params Type[] excludedTypes)
{
List<Type> listExcludedTypes = excludedTypes.ToList();
ApplicationData appData = workspace.ApplicationData;
IEnumerable<IDataServiceProperty> properties = appData.Details.Properties.All();
foreach (IDataServiceProperty prop in properties)
{
dynamic entitySet = prop.Value;
Type entityType = entitySet.GetType().GetGenericArguments()[0];
if (!listExcludedTypes.Contains(entityType))
{
typeof(DataUtilities).GetMethod("DeleteSet", BindingFlags.Static | BindingFlags.Public)
.MakeGenericMethod(entityType)
.Invoke(null, new object[] { entitySet });
}
}
appData.SaveChanges();
}
public static void DeleteSet<T>(this EntitySet<T> entities) where T:
IDispatcherObject, IObjectWithDetails, IStructuralObject, INotifyPropertyChanged, IBusinessObject, IEntityObject
{
List<T> entityList = entities.Select(e => e).Execute().ToList();
int entityCount = entityList.Count();
for (int i = 0; i < entityCount; i++)
{
T entity = entityList.ElementAt(i);
if (entity != null)
{
// Uncomment the line below to see all entities being deleted.
// Debug.WriteLine("DELETING " + typeof(T).Name + ": " + entity);
entity.Delete();
}
}
}
}
}
Do step 1 again, but this time under yourAppName.DesktopClient. You should now have 2 folders named UserCode, one in both side of the application.
Right-click on that last folder (UserCode in yourAppName.DesktopClient), go to Add and then Existing Element... .
Navigate to ...\yourAppName\yourAppName.Server\UserCode.
Select DataUtilities.cs, and click on the little down arrow besides the Add button. Choose Add as link. Now the class can be used on both Server side AND Client Side.
Now let's use the new extension methods !
Back in the solution explorer, Right-click on yourAppName.DesktopClient, and select Show Application Code (Should be the first option in the dropdown menu).
Replace the generated code with this (Or, if you had some custom code already in that class, add the single line I show in Application_Initialize()):
using LightSwitchApplication.UserCode;
namespace LightSwitchApplication
{
public partial class Application
{
partial void Application_Initialize()
{
Current.CreateDataWorkspace().DeleteAllSets();
}
//Some other methods here if you already modified this class.
}
}
Voila ! The next time you start your application, all data stored in the intrinsic database should be gone.
More info on the code:
How it works
I won't explain the whole process here but basically:
DeleteAllSets(...) will get all the EntitySets of the data source and call DeleteSet(...) on each one of them.
DeleteSet(...) will call the already existing Delete() method on each entity in the EntitySet.
How to exclude data from deletion
You can also pass in Type parameters to the DeleteAllSets(...) method to exclude those from the deletion process:
Lets say I have 2 tables storing employees data and products data respectively. Let those tables be called Employee and Product. If, for example, I had added test data in the Product table, and wanted to get rid of it, without deleting all my employees, I'd use the extension method like this:
Current.CreateDataWorkspace().DeleteAllSets(typeof(Employee));
This would delete all the entities in the Product table only.
I hope this helps anyone stuck with Lightswitch's not-so-easy debugging and testing ! The whole process is probably translatable to the Web version, but I'll leave that to someone else.
Related
I am just getting started with EF Core and Net Core in general an ran into a problem I could not find any answers for.
I am working on a console application that uses a SQLite database for storage. I am testing things right now and working with the context works fine. My example program below runs fine. Note that I did use migrations to create the database initially.
Now eventually when I finish this App I want to make sure that the database exists. As read in other posts this should be done with ctx.Database.Migrate(). I cannot access this method yet however. So my question is what do I have to do to access it? Am I missing a package that adds an extension method? Do I need to configure more things?
Please excuse this very basic question but I could not find anything regarding this. So if I just don't know where to look I would also be glad about a reading recommendation.
using System;
using MyLog.NetCore.Models;
using MyLog.NetCore.DataAccess;
namespace MyLog.NetCore
{
internal class Program
{
#region Private Methods
private static void Main(string[] args)
{
using (var ctx = new MyLogContext())
{
ctx.Add(new PartialLogEntry { PartialLogEntryID = 1, StartDateTime = 1, Title = "Test" });
var count = ctx.SaveChanges();
Console.WriteLine($"{count} changes saved to database!");
Console.WriteLine();
Console.WriteLine("All partial lof entries in database:");
foreach (var entry in ctx.PartialLogEntries)
{
Console.WriteLine($"ID: {entry.PartialLogEntryID}\tStart: {entry.StartDateTime}\tTitle: {entry.Title}");
}
}
Console.ReadLine();
}
#endregion Private Methods
}
}
Many EF Core methods are implemented as extension methods. So to make them available, this first thing you need is:
using Microsoft.EntityFrameworkCore;
This particular method is defined in RelationalDatabaseFacadeExtensions residing in the Microsoft.EntityFrameworkCore.Relational assembly, so make sure you are referencing it.
OK... so here is something I do not even know if it is even possible. Is it possible to upload an image file during the initial phase of a code first migration? For instance, when creating an initial site user or an admin user that has a portrait image, would it be possible to upload that image during the initial creation of that user?
I couldn't find anything relevant in SO or online that would even come close to even suggest a viable solution so this might be the very first time something like this has even been attempted.
First, create the new migration file (or use the existing one).
Inside Up() method you can put code for
file upload, and inside Down() method code for file removal from
repo (in case you want to revert migration).
Below is one of the many possible ways to do some remote upload, this is one of the simplest:
using (var webClient = new WebClient())
{
webClient.UploadFile("ftp://localhost/samplefile.jpg", "samplefile.jpg");
}
For this to work you should add using System.Net; to the migration file. Also, obviously you need to handle upload permissions and credentials, depending on the type of remote repo you are using.
EDIT:
Using File object is even more trivial. Here is the complete code for migration class:
using System;
using System.Data.Entity.Migrations;
using System.IO;
public partial class MigrationWithFileCopy : DbMigration
{
public override void Up()
{
File.Copy("sourceFile.jpg", "destinationFile.jpg");
}
public override void Down()
{
File.Delete("destinationFile.jpg");
}
}
First thing to say is that I can't use built in Properties.Settings, as these settings are stored in an XML file which are used by several people (not at the same time but via version control).
I am developing a code generation tool which allows the users to specificy the output locations of all the generated files, along with other project specific settings.
Currently, in the application I have a static class called ProjectSettings which has a public static parameter for each setting using the in program:
public static string Settings_ScreenDefinitions_C;
public static string Settings_ScreenDefinitions_H;
// Etc currently there are about 20 of these.
Is there a better way of storing and accessing these thoughout the project?
Secondly these settings are stored in an XML using a read/write call for each setting in the above ProjectSettings class:
xmlWriter.WriteElementString("ScreenDefinitionsFileC", ProjectSettings.Settings_ScreenDefinitions_C);
xmlWriter.WriteElementString("ScreenDefinitionsFileH", ProjectSettings.Settings_ScreenDefinitions_H);
// Again there is around 20 of these, one for each property and the same for reading them back out.
As I add more properties I can't help but feel that there must be a more elegant way of constructing the class (maybe a dictionary?) and a better way of using that within the XML for saving and reading?
Any suggestions/pointers would be great.
Here is how I would do it with an enum and a dictionary. I've used console output to show how you can write the key and value of the dictionary.
using System;
using System.Collections.Generic;
namespace ConsoleApplication
{
class Program
{
private enum Settings
{
ScreenDefinitionsFileC,
ScreenDefinitionsFileH
};
static void Main(string[] args)
{
var settings= new Dictionary<Settings, string>()
{
{Settings.ScreenDefinitionsFileC, "Setting 1"},
{Settings.ScreenDefinitionsFileH, "Setting 2"}
};
foreach (var setting in settings)
{
Console.WriteLine("{0} {1}", setting.Key, setting.Value);
}
Console.ReadKey(true);
}
}
}
This way you can enumerate your settings when writing to XML, but can also look-up specific settings during the life cycle of your program.
I don't know the whole list of the configurations, but I hope most of them may be controlled by adding different configuration in configurationManger, that is if you add "MyConfiguration" you can open afterwards project settings (especially pre-build and post build event)and the configurations there are all only for "myConfiguration" so switching between different configurations is only by selecting different configuration.
The team of developers I work with are using SQL Data Projects for a large piece of work we have to do against an existing database. We are a few weeks in and there have been a few gotchas, but the experience has been generally good.
However, when we get to deploy to production, the dba team have refused to accept DACPACs as a deployment method. Instead, they are want to see a traditional script per DML or DDL statement.
The current thinking is to create a difference script between the finished SQL project and the production environment, and then parse that into individual scripts. Not nice I know.
To parse the difference script there seems to be two options:
Parse the script based on the batch separator command, GO. A rather basic solutions but shows promise.
Or, use the Microsoft.SqlServer.TransactSql.ScriptDom. This looks more future proof but seems far more complex.
I'm trialling the ScriptDom at the moment but am having trouble understanding it. My current, but not only issues, is as follows.
I'm trying to parse the following SQL using the ScriptDOM in C#:
CREATE TABLE dbo.MyTable
(
MyColumn VARCHAR(255)
)
But cannot see how to access the VARCHAR size, in this case, 255.
The code I'm using is as follows:
TSqlFragment sqlFragment = parser.Parse(textReader, out errors);
SQLVisitor myVisitor = new SQLVisitor();
sqlFragment.Accept(myVisitor);
public override void ExplicitVisit(CreateTableStatement node)
{
// node.SchemaObjectName.Identifiers to access the table name
// node.Definition.ColumnDefinitions to access the column attributes
}
From each column definition I expected to find a length property or similar. However, I also have a sneaking suspicion that you can use the Visitor Pattern, which I struggle with, to reparse each column definition.
Any ideas?
I don't think you need a visitor here at all. If I understand your goal correctly, you'd like to take the TSQL generated by SSDT, parse it using SQLDOM and then print the batches individually. The code to do that would look something like this:
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.SqlServer.TransactSql.ScriptDom;
namespace ScriptDomDemo
{
class Program
{
static void Main(string[] args)
{
TSql120Parser parser = new TSql120Parser(false);
IList<ParseError> errors;
using (StringReader sr = new StringReader(#"create table t1 (c1 int primary key)
GO
create table t2 (c1 int primary key)"))
{
TSqlFragment fragment = parser.Parse(sr, out errors);
IEnumerable<string> batches = GetBatches(fragment);
foreach (var batch in batches)
{
Console.WriteLine(batch);
}
}
}
private static IEnumerable<string> GetBatches(TSqlFragment fragment)
{
Sql120ScriptGenerator sg = new Sql120ScriptGenerator();
TSqlScript script = fragment as TSqlScript;
if (script != null)
{
foreach (var batch in script.Batches)
{
yield return ScriptFragment(sg, batch);
}
}
else
{
// TSqlFragment is a TSqlBatch or a TSqlStatement
yield return ScriptFragment(sg, fragment);
}
}
private static string ScriptFragment(SqlScriptGenerator sg, TSqlFragment fragment)
{
string resultString;
sg.GenerateScript(fragment, out resultString);
return resultString;
}
}
}
As for how to work with these ASTs, I find it easiest to use Visual Studio's debugger to visualize the tree, because you can see the actual type of each node and all of its properties. It takes just a little bit of code to parse the TSQL, as you can see.
Great that you are using ssdt!
The easiest way to handle this when you have DBA's who don't want to work with dacpacs is to pre-generate the deloyment script using sqlpackage.exe.
The way I do it is...
Check t-sql code into project
Build server builds ssdt project
Deploy and run tests on ci server
use sqlpackage.exe /action:script to compare the dacpac to QA, PROD etc and generate a deployment script.
The DBA's then take that script (or when we are ready we tell them the build number to grab) - they can the peruse and deploy that script.
Things to note:
You will need access to the prod db or a mirror copy you can use, you do not need dbo or anything just permissions in (https://the.agilesql.club/Blogs/Ed-Elliott/What-Permissions-Do-I-Need-To-Generate-A-Deploy-Script-With-SSDT)
The scripts are only valid until the schema in the prod db changes - so if you generate 4 scripts the and run script 1, the other three are invalid and you will need to re-run a build to generate the script.
If you don't have CI setup you can just use sqlpackage.exe to generate the script without the automatic bits :)
Hope it helps!
ed
#reference Microsoft.SqlServer.BatchParser
#reference Microsoft.SqlServer.BatchParserClient
using System;
using System.Collections.Specialized;
using System.IO;
using System.Text;
using Microsoft.SqlServer.Management.Common;
namespace ScriptParser
{
class Program
{
static void Main(string[] args)
{
ExecuteBatch batcher = new ExecuteBatch();
string text = File.ReadAllText("ASqlFile.sql");
StringCollection statements = batcher.GetStatements(text);
foreach (string statement in statements)
{
Console.WriteLine(statement);
}
}
}
}
This may be a long shot, but I'm using ComponentOne's Spellchecker control for Silverlight. I made a test project, added a plain textbox and a button to it, added the references to the C1.Silverlight and C1.Silverlight.SpellChecker bits, and added the dictionary file to my project.
In the code, I called up the spellchecker on button1's click event and it worked SPLENDIDLY. The spellchecker dialog shows up, and works exactly as it should.
Since that test was successful, I then tried to implement this into my existing project. I've had no success for absolutely NO reason that I can determine, since I used the EXACT SAME code.
Here's the code I use to call the component:
using C1.Silverlight;
using C1.Silverlight.SpellChecker;
using C1.Silverlight.Resources;
public partial class MainPage : UserControl
{
C1SpellChecker spellChecker = new C1SpellChecker();
public MainPage()
{
InitializeComponent();
spellChecker.MainDictionary.LoadAsync("C1Spell_en-US.dct");
}
private void btnSpelling_Click(object sender, RoutedEventArgs e)
{
var dlg = new C1SpellDialog();
spellChecker.CheckControlAsync(txtArticle, false, dlg);
}
The references to C1.Silverlight and C1.Silverlight.Spellchecker are added to this project as well, and the dictionary as been added in the same fashion as well. The issue seems to be that for whatever reason the dictionary is not loading, because the spellChecker.Enabled method returns whether or not the main dictionary has been loaded. If I call MessageBox.Show("SpellChecker Enabled = " + spellChecker.Enabled.ToString()); it shows false, even though the call to load the dictionary is there (as you can see).
What would cause the dictionary to not load? Have I added it to my project incorrectly somehow?
EDIT: I suspect that I have added the dictionary to the project incorrectly, because the ComponentOne reference states:
If C1SpellChecker cannot find the
spelling dictionary, it will not throw
any exceptions. The Enabled property
will be set to false and the component
will not be able to spell-check any
text.
I just don't know what's wrong though because it was added in the same way that it was in the test project (Right clicked on the project.web->Add->Existing Item)
As always, thank you!
-Sootah
You could add the dictionary to the Silverlight app as an embedded resource and then load it using this code:
public MainPage()
{
InitializeComponent();
// load C1SpellChecker dictionary from embedded resource
var asm = this.GetType().Assembly;
foreach (var res in asm.GetManifestResourceNames())
{
if (res.EndsWith(".dct"))
{
using (var s = asm.GetManifestResourceStream(res))
{
sc.MainDictionary.Load(s);
break;
}
}
}
}
I think this post is duplicated in our forum as well, but will answer first here. Please try this:
1) Try to access the .dct file using your browser. If you cannot see it, it's probably because your web server is not serving that type of files. You need ton configure the web server to allow it.
2) verify the URL you are using is correct.http://helpcentral.componentone.com/CS/silverlight_161/f/78/p/86955/241328.aspx#241328
3) Check you are setting everything correctly: http://helpcentral.componentone.com/CS/silverlight_161/f/78/p/81924/227790.aspx#227790
Hope this helps!