Disable the default constructor on a Database-First workflow - entity-framework

How can I prevent the use of the parameterless constructor of the generated DbContext?
var dcx = new DataEntities();
The default constructor is generated by the T4 template, and I thus cannot override it in a partial class. I would prefer it not compile, but a runtime error would also be good.

You can modify the template to provide the constructor you want.
Open the *.Context.tt file
Go to line ~59
Change this code.
public <#=code.Escape(container)#>()
: base("name=<#=container.Name#>")
Into the default constructor you want, for example.
public <#=code.Escape(container)#>(string nameOrConnectionString)
: base(nameOrConnectionString)
Save

You can inherit the DbContext created by the template, define your own constructor, and use the inherited DbContext instead of the one generated by the template.
public class MyModifiedDbContext : TheTemplateGeneratedDbContext
{
public MyModifiedDbContext()
{
// define your own constructor
}
}
Or make it private to avoid its use, so you get the error at compile time
public class MyModifiedDbContext : TheTemplateGeneratedDbContext
{
private MyModifiedDbContext()
// ...
}
Use MyModifiedDbContext instead of TheTemplateGeneratedDbContext

I gave up waiting for EF Core team to add this as an option. I don't want to make and maintain my own T4 templates for this - that's nuts!
Solution for me was just to run some regex on the generated code as part of a powershell script.
fix-dbcontext.ps1
$filename=$args[0]
# load context file
$content = (Get-Content -Raw $filename)
[regex] $commentOutConstructorRegex = '(?ms)(?<=: DbContext\s*.*?)(public.*?)(?=\s*public)'
$content = $commentOutConstructorRegex.Replace($content, '// Default constructor removed', 1)
[regex] $removeOnConfiguringRegex = '(?ms)(protected override void OnConfiguring).*?(?=\s*protected)'
$content = $removeOnConfiguringRegex.Replace($content, '// Generated OnConfiguring removed', 1)
[regex] $dateCommentRegex = '(?ms)(?=\s*public partial class)'
$content = $dateCommentRegex.Replace($content, "`r`n`t// Generated " + (Get-Date).ToString() + "`r`n", 1)
$content | Out-File -Encoding UTF8 $filename
This will:
Remove the default constructor
Remove the OnConfiguring method including hardcoded connection string
Add the date in a comment
Just run it with .\fix-dbcontext.ps1 .\MyDBContext.cs.
You probably want to change that last line to context.txt instead of $filename until you're sure it does what you want.
IMPORTANT: This was tested only on EFCore templates, but if you understand my Regexes you should be able to modify it for EntityFramework if it doesn't already work.

Related

Is there a way to add a method to built-in/native powershell object (or type?/class¿)?

Is there a way to add a method to built-in/native powershell object (or type?/class¿)?
In particular, I'm looking at you [hashtable], but I suppose my question is also a general one... for instance, I could see wanting to add functionality to all my arrays...
For instance, I would like all my [hashtable] objects to have the method: .AddAndOverwrite(..) which would replace the value of the key if it exists; otherwise it creates a new key.
The only way I seem to be able to do this is to:
create an empty hashtable, $HashTableNew
add the ScriptMethod(s) to $HashTableNew (i.e. .AddAndOverwrite(..))
then when I use it, make a copy of $HashTableNew
$SomeOtherHashTable = $HashTableNew.PSObject.Copy()
This just seems like not "the way"...
Note: I will admit, this is not the best example use of a data type extension method (as #SantiagoSquarzon points out)... but it is a simple one, and it allows for a simple example in the accepted answer; so I'm intentionally leaving it as is, rather than changing question / the extension method to .foo() returning widgets...
There is indeed a better and easier way to update a type as a whole by using Update-TypeData.
Here is an example that add an .AddOrOverwrite method to the hashtable.
$TypeParam = #{
TypeName = 'System.Collections.Hashtable'
MemberType = 'ScriptMethod'
MemberName = 'AddOrOverwrite'
Value = { Param($Key, $Value) $this.$key = $Value }
}
Update-TypeData #TypeParam -Force
$SomeHashTable.AddOrOverwrite('aaa','2222222')
$this, in the scriptblock of the method definition, correspond to the object reference that is targeted, in this case, the hashtable.
-Force will overwrite the definition every time without error stating the type was already added.
That method is not super useful as it does something that the hashtable manage pretty well on its own by just using assignment but it demonstrates how to do it.
Bonus example
Here's an example on how you would apply this principle and create 2 script properties (readonly) for a string so you can convert to base 64 back and forth.
$TypeParam = #{
TypeName = 'System.String'
MemberType = 'ScriptProperty'
Force = $true
}
Update-TypeData #TypeParam -MemberName 'Base64' -Value { [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($this)) }
Update-TypeData #TypeParam -MemberName 'Base64Decoded' -Value { [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($this)) }
# Encode the string to base 64 (Output: U29tZVN0cmluZw==)
"SomeString".Base64
# Decode the string from Base64 (Output: SomeString)
"U29tZVN0cmluZw==".Base64Decoded
References
Msdocs - About-Types
Dr Scripto - Easily Update Powershell Type Data by Using a Cmdlet

How to create a case sensitive Hashtable without generating a PSScriptAnalyzer warning?

By default hash tables created by PowerShell (e.g. $hashtable = #{}) are case insensitive.
For my script I want to explicitly create a case sensitive hash table.
This can be done by:
$hashtable = [hashtable]::new()
or:
$hashtable = New-Object hashtable
But I want to have my script also compliant with the default PSScriptAnalyzer rules. For the above case sensitive hash table examples, the UseLiteralInitializerForHashtable rule cases a warning:
Use literal initializer, #{{}}, for creating a hashtable as they are case-insensitive by default
I would expect to be able to work arround this by specifying the StringComparer, like:
[HashTable]::New(0, [StringComparer]::Ordinal)
But this still generates an error (although [HashTable]::New(0, [StringComparer]::OrdinalIgnoreCase) doesn't).
AFAIK, there is not something like: [StringComparer]::OrdinalMatchCase, or?
How to create a case sensitive Hashtable without generating a PSScriptAnalyzer warning?
PSScriptAnalyzer version: 1.18.3
Tested both Windows PowerShell (5.1) and PowerShell Core (6.2.3)
Steps to reproduce the warning:
Invoke-ScriptAnalyzer -ScriptDefinition '[HashTable]::New(0, [StringComparer]::Ordinal)'
Have you tried to encapsulate the hashtable using inline c# as a custom static class?
Add-Type -typedef #"
using System;
using System.Collections;
namespace myCsharp
{
//-----------------------------------------
public class myHashtable
//-----------------------------------------
{
//-------------------------------------
public static Hashtable GetHashtable()
//-------------------------------------
{
Hashtable ht = new Hashtable( 0, StringComparer.Ordinal);
return ht;
}
}
}
"#
$x = [myCsharp.myHashtable]::GetHashtable()

Powershell parsing of a C# file to get values of constants

I need to parse some C# files to get the values of some constant variables. I know I can do something like
$input = Get-Content C:\somefile.cs ...
then loop over each line and do some text matching.
...but was wondering whether I can utilize some sort of a C# DOM object to get the values of constants?
You can load the type dynamically from the powershell command line, and then evaluate the constant.
Example:
C:\somefile.cs contents:
public static class Foo
{
public const string SOME_CONSTANT = "StackOverflow";
}
Powershell command line:
Add-Type -Path C:\somefile.cs
$constantValue = [Foo]::SOME_CONSTANT

Zend Framework 2 - Instantiating Class

Why do I have to provide the leading backslash when providing the path to a class? [\Zend\Validator\EmailAddress() and not just Zend\Validator\EmailAddress()]
$validator = new \Zend\Validator\EmailAddress();
$email = "example#example.com";
if ($validator->isValid($email)) {
// email appears to be valid
} else {
// email is invalid; print the reasons
foreach ($validator->getMessages() as $messageId => $message) {
echo "Validation failure '$messageId': $message\n";
}
}
Thanks!
You said you've declared the namespace of Blog\Controller at the start of the class, with:
namespace Blog\Controller
This tells PHP that by default all classes referenced within that class are within the Blog\Controller namespace. So if you were to then do:
$validator = new Zend\Validator\EmailAddress();
what you are actually doing is:
$validator = new Blog\Controller\Zend\Validator\EmailAddress();
which would give you an error since that class doesn't exist.
If you prefix a class with the backslash, you're telling PHP that that class is not in your declared namespace, but instead is in the global space, which is why it then works.
You also said you've imported the EmailAddress class using use Zend\Validator\EmailAddress. This allows you to reference that class as simply 'EmailAddress'. So you change your code to:
$validator = new EmailAddress();
which is much more concise. This is one of the advantages of namespaces - it allows you to use shorter class names in your code.

How to get Model Object using its name from a variable in Laravel 5?

I am trying to get information from a model using its name which is sent as parameter from blade using ajax call.
$.get("{{ url('auditInformation')}}", {modelName: modelName,versions:versions,currentData:currentData[index]});
Now i need to retrieve information using modelName from a model.
So when i tried this:
$auditInfo=Input::all();
$modelName=$auditInfo['modelName'];
$values=$modelName::find(1);
I got this response Class 'Designation' not found
But if i use
$modelName=new Designation();
$values=$modelName::find(1);
then it shows data exactly what i want.
So i understand that this is all about model ( class ) object.
Is there any way to assign object to $modelName using $auditInfo['modelName'] .
Thanks
If you want to do that way, you should use the model's namespace.
For example, if the 'Destination' model's namespace is app\Destination, you should use like this :
$auditInfo=Input::all();
$appPrefix = 'app';
$modelName=$appPrefix . '\' . $auditInfo['modelName'];
$values=$modelName::find(1);
This seems to be working
$table_name = "App\Models\PeopleYouMayLikeModel";
$obj = $table_name::where($column_name_identifier_1, '=', $row_identifier_1)
->where($column_name_identifier_2, '=', $row_identifier_2)->first();
The single backslash between the singe quote is considered as an escape sequence so use double backslash is 100% work. For example
public function index(Request $request) {
$model_prefix="App\Models";
$modal = $model_prefix.'\\'.$request->type;
$modal::updateOrcreate(['users_id'=>session("user")->users_id],$request->all())->save();
dd(Profile::all());
}
//Laravel 7.0