I have a WPF application. It has many blocks that access the database. The connection string is determined by a setting in my App.config file.
using (var dbContext = new MyEntities())
{
// ...
}
But now I need the ability to switch between different databases at run time. I've updated the code to manage a list of databases, along with a connection string to each one.
My question is, is there any way to have the existing code using the connection string indicated by my code without having to change the existing code? For example, perhaps I can create a static connection string property in my entities class, and then modify that class to use the property each time it connects to the database.
Has anyone done this? Any tips to do this in a painless way?
So the best idea I came up with was to add a static ConnectionString property to my entities class. And then modify the constructor to pass this property to the base class.
One issue is that any change to the entities class would be overwritten by the T4 scripts when the model is updated. You could create a partial class and define your own constructor, but if that constructor has no arguments then it will conflict with the existing constructor.
My solution for this was to actually edit the existing T4 script directly, as shown below.
<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext
{
// Original code
//public <#=code.Escape(container)#>()
// : base("name=<#=container.Name#>")
//{
// Modified code
public static string ConnectionString { get; set; }
public <#=code.Escape(container)#>()
: base(ConnectionString)
{
// End of modified code
<#
if (!loader.IsLazyLoadingEnabled(container))
{
#>
this.Configuration.LazyLoadingEnabled = false;
<#
}
When you save the T4 script, it will immediately regenerate the code.
Related
In an old WPF project I have a class with Properties like this:
private string _name = "";
public string Name
{
get { return _name; }
set
{
string cleanName = clsStringManip.CleanText(value, true);
if (cleanName != _name)
{
_name = cleanName;
}
}
}
Where every time the name changes, I ensure that the value is "cleaned". Putting it in the property ensures I never forget to clean the string before setting the property on the object.
Now I am recreating this system using MVC5 and EntityFramework6.1 using DatabaseFirst.
So all the properties are autogenerated by EF. How then can I add the equivalent CleanText function to my properties without editing the autogen code? - as I'll lose these changes next time I change my database and resync.
All I can find via Google is a way add data annotations via MetadataType and partial classes but this doesn't answer my question.
I tried to add the above code into a partial class but get the error:
The type XXX already contains a definition for Name
The only way I can think is to create a bunch of SetProperty() functions but this is dirty and you can never ensure other developers (or myself) will remember to use them.
Disclaimer: I haven't used EF 6 yet.
Let me answer this in two parts. First, I will tell you how to do this. Then I will tell you why I don't think you should do this. :-)
HOW:
As you discovered, you cannot create another Name property. You need to modify the way the EF generates the code, so that it gives you a place to insert your new code. Depending on how you are using the EF, it often generates Validate() method calls or OnPropertyChanged() calls. You may be able to do what you want inside of those methods.
If you can't do this in Validate() or OnPropertyChanged(), you could change the T4 template to generate something like this:
private string _name = "";
public string Name
{
get { return _name; }
set
{
string cleanName = value;
Cleanup_Name(ref cleanName);
if (cleanName != _name)
{
_name = cleanName;
}
}
}
private partial void Cleanup_Name(ref string);
This gives you a partial method that you can then implement as you see fit. So for any property you want to customize, you can now add another file to your project that does this:
public partial class MyEntity {
void Cleanup_Name(ref string name)
{
// Put your logic in here to fixup the name
}
}
If you do not write the above code block, then the partial method is simply a no-op. (Partial methods must return void, hence the use of a ref parameter).
WHY NOT?
The advantage of this method is that it is totally transparent to the developer. The property is just magically changed. But there are several disadvantages:
Some controls expect that if they call name = "123" that if they get the name back, it is "123" and will fail if this happens. Values are changing but no PropertyChanged event fired. If you do fire the PropertyChanged, then they sometimes change the value back. This can cause infinite loops.
There is no feedback to the user. They typed in one thing, and it looked right, but now it says something different. Some controls might show the change and others won't.
There is no feedback to the developer. The watch window will seemingly change values. And it is not obvious where to see the validation rules.
The entity-framework itself uses these methods when it loads data from the database. So if the database already contains values that don't match the cleanup rules, it will clean them when loading from the database. This can make LINQ queries misbehave depending on what logic is run on the SQL server and what logic is run in the C# code. The SQL code will see one value, the C# will see another.
You might also want to look into what the Entity-Framework's change tracking does in this case. If a property set does a cleanup while loading values from the database, does it consider that a change to the entity? Will a .Save() call write it back to the database? Could this cause code that never intended to change the database to suddenly do so?
ALTERNATIVE
Instead of doing this, I suggest creating a Validate() method that looks at each property and returns errors indicating what is wrong. You could also even create a Cleanup() method that fixes the things that are wrong. This means the cleanups are no longer transparent, so the developer must call them explicitly. But that is a good thing: the code isn't changing values without them realizing it. The person writing the business logic or the UI knows at what point the values will change, and can get a list of why.
The only way you can achieve this is by creating a new property you actually use in your application. Perhaps you can hide the original property in the designer. The actual property you use could look like this:
public string ExternalName
{
get { return Name; }
set
{
string cleanName = clsStringManip.CleanText(value, true);
if (cleanName != Name)
{
Name = cleanName;
}
}
}
As an alternative, you can use POCO classes:
If you want to keep using database-first, check this answer
Use code-first for an existing database, see this detailed guide
Add partial to the generated class.
Change the scope of Name in the generated class from public to internal.
Add the following in the same assembly:
public partial class classname
{
[NotMapped]
public string CleanName
{
get { return Name; }
set
{
var cleanName = clsStringManip.CleanText(value, true);
if (cleanName != Name)
Name = cleanName;
}
}
}
Caveat: you'd have to remember to do steps 1-2 every time you regenerated your POCOs ... I'd seriously consider Code First to Existing Database.
EDIT
Optionally:
Rename Name as InternalName in the generated classname; decorate it with [Column("Name")].
Rename CleanName as Name in the partial class under your control.
Caveat in 4 becomes "remember to do steps 1, 2, and 5 every time you regenerate POCOs".
This approach has the added benefit of not having to modify any of your client code (i.e., use of Name remains Name). And I'd still strongly consider Code First to Existing Database.
Let me preface this by saying I'm kind of new to C# and database implementation. I'm trying to create some code that will connect to a database, but the type of database can vary between SQL Server, SQL Server CE, and maybe later on MySQL or something like that. I'm trying to implement the code using the DbConnection, DbCommand, DbDataAdapter, etc classes that is the base class of all these types of database classes. However, I am having difficulty trying to set this up.
I've tried using DatabaseFactory.CreateDatabase("connectionString") but it wants me to put connectionString the the app.config file, but I don't want the program to be set up that way.
I've also tried DbProviderFactories.GetFactory(providerStr) but in order to get the correct providerStr, I need to know which database I'm using.
Basically I want it so that no matter what type of database I am using, the code will still work. Is there any easy way to do this or is it possible? Any help is appreciated.
I've come to the conclusion that there is no way to do this without exactly specifying the type of database to connect to. So my answer was to add an enumeration of the varying types of databases and use DbProviderFactories.GetFactory(providerStr) to create a DbProviderFactory instance. Here's some of the code if anyone is interested or trying to do what I'm doing.
DbProviderFactory dbFactory;
private DatabaseTypes m_dbType;
public DatabaseTypes DbType
{
get { return m_dbType; }
set
{
m_dbType = value;
dbFactory = DbProviderFactories.GetFactory(DatabaseProvider.GetDatabaseProvider(m_dbType));
}
}
In another file I have the enumeration and DatabaseProvider class defined. It looks like this:
public enum DatabaseTypes
{
SQL,
SQL_CE
}
public static class DatabaseProvider
{
private const string SQL_Provider = "System.Data.SqlClient";
private const string SQL_CE_Provider = "System.Data.SqlServerCe.3.5";
public static string GetDatabaseProvider(DatabaseTypes dbType)
{
switch (dbType)
{
case DatabaseTypes.SQL_CE:
return SQL_CE_Provider;
case DatabaseTypes.SQL:
default:
return SQL_Provider;
}
}
}
Now if I want to add another database type, I'll just add it to the enumeration, get add another provider string, and put another case in the switch statement! All I need to do now is figure out how I am going to handle the queries.
UserAccount objUserAccount=null;
AutoMapper.Mapper.CreateMap<AccountBO, UserAccount>();
objUserAccount = AutoMapper.Mapper.Map<AccountBO, UserAccount>(lstAcc[0]);
Up to this point it is mapping AccountBO properties fine.
Now I have to map object objAddressBO properties to destination including above mapped values. for this I have written code as below following to above lines of code.
AutoMapper.Mapper.CreateMap<AddressBO,UserAccount>();
objUserAccount=AutoMapper.Mapper.Map<AddressBO,UserAccount>(objAddressBO);
But it's losing first time mapped values and returning only the last time mapped values.
Please let me know what changes I need to do to have both the values in my destination object.
You should only configure the mapping once. The best way to do this is by using profiles:
public class MyProfile : Profile
{
public override string ProfileName
{
get
{
return "MyProfile";
}
}
protected override void Configure()
{
AutoMapper.Mapper.CreateMap<AccountBO, UserAccount>();
AutoMapper.Mapper.CreateMap<AddressBO,UserAccount>();
}
}
This should then be initialised in an initialisation method (such as App_Start for web projects)
You should also create a unit test to test the mapping has been configured correctly
[TestFixture]
public class MappingTests
{
[Test]
public void AutoMapper_Configuration_IsValid()
{
Mapper.Initialize(m => m.AddProfile<MyProfile>());
Mapper.AssertConfigurationIsValid();
}
}
If that all works fine, and assuming I have understood the question correctly, you want to initialise objUserAccount from listAcc[0], then fill in some additional parameters from objAddressBO. You can do this like:
objUserAccount = Mapper.Map<AccountBO, UserAccount>(lstAcc[0]);
objUserAccount= Mapper.Map(objAddressBO, objUserAccount);
The first map will create the object, and the second map will update the provided destination object.
Note that for this to work correctly you may need to fill out your mapping configuration a little to provide the correct behaviour. For example, if you wish to avoid updating a destination property you can use the UseDestinationValue directive. If you want to apply a condition to the update you can use the Condition directive. If you wish to ignore the property completely, you can use the Ignore directive.
If required, more documentation can be found here.
According to Microsoft:
http://msdn.microsoft.com/de-de/library/system.data.linq.mapping.columnattribute.expression.aspx
It's possible to add expression to the Linq-to-SQL Mapping.
But how to configure or add them in Visual Studio in the Designer?
Problem, when I add it manual to thex XYZ.designer.cs it on change it will be lost.
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:2.0.50727.4927
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
This is generated:
[Column(Name="id", Storage="_id", DbType="Int")]
public System.Nullable<int> id
{
...
But i need something like this
[Column(Name="id", Storage="_id", DbType="Int", Expression="Max(id)")]
public System.Nullable<int> id
{
...
Thanks.
According to this article:
http://msdn.microsoft.com/en-us/library/system.data.linq.mapping.columnattribute.expression.aspx
you should use the ColumnAttribute.Expression Property when you use CreateDatabase to define a column as containing computed values.
So you should check this article:
http://msdn.microsoft.com/en-us/library/Bb399420%28v=VS.100%29.aspx
Another way is to define expression on your sql server so it'll be mapped by the LINQ designer.
Edit: mmmm you edited your question, so probably my answer is not gonna help you so much, but you might be able to do this anyway with your 'extended' question :D
I do this by adding another class file to the project, give them the same name as the object from LinQ-to-SQL you want to extend and define it as partial.
for example, if you have a table called Files, the object File will be created for you by L2S. If you then create a file (with the same namespace as your DataContext object), and make it partial, like this:
public partial class File
{
}
You can just add properties, methods, etc. From within this class, you also have direct access to the properties of the 'other' File class.
It's a little klugy, but in your linq2sql designer, rename the field from 'id' to 'xid' (or anything else) and change its accessibility to internal.
then, in another file, start another partial class, like Wim Haanstra showed, and create a new property called 'id', add all the attributes you want, and in the get & set, just map it to and from the original property, now called 'xid'.
it would look something like this:
public partial class File
{
public int? id
{
get { return xid; }
set { xid = value; }
}
}
this is more commonly done to map fields in the database to a different type in the object, e.g. an int in the DB to an enum in the object, a byte/smallint/etc. in the DB, a boolean in the object. or to add attributes, like [DataMember] to the property.
I have a class reflecting my dbml file which extends DataContext, but for some strange reason it's telling me
System.Data.Linq.DataContext' does not contain a constructor that takes '0' arguments"
I've followed various tutorials on this and haven't encountered this problem, and VS doesn't seem to able to fix it.
Here's my implementation
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Reflection;
using System.Text;
using IntranetMvcAreas.Areas.Accounts.Models;
namespace IntranetMvcAreas
{
partial class ContractsControlDataContext : DataContext
{
[FunctionAttribute(Name="dbo.procCC_Contract_Select")]
[ResultType(typeof(Contract))]
[ResultType(typeof(ContractCostCentre))]
[ResultType(typeof(tblCC_Contract_Data_Terminal))]
[ResultType(typeof(tblCC_CDT_Data_Service))]
[ResultType(typeof(tblCC_Data_Service))]
public IMultipleResults procCC_Contract_Select(
[Parameter(Name = "ContractID", DbType = "Int")] System.Nullable<int> ContractID,
[Parameter(Name = "ResponsibilityKey", DbType = "Int")] System.Nullable<int> ResponsibilityKey,
[Parameter(Name = "ExpenseType", DbType = "Char")] System.Nullable<char> ExpenseType,
[Parameter(Name = "SupplierID", DbType = "Int")] System.Nullable<int> SupplierID)
{
IExecuteResult result = this.ExecuteMethodCall(this, (MethodInfo)(MethodInfo.GetCurrentMethod()), ContractID, ResponsibilityKey, ExpenseType, SupplierID);
return (IMultipleResults)result.ReturnValue;
}
}
}
And it's ContractsControlDataContext that's pointed at as the problem
(btw, this has no relation to a very recent post I made, it's just I'm working on the same thing)
EDIT
It's probably worth clarifying this, so please read very carefully.
If you do not extend DataContext in the partial class, then ExecuteMethodCall isn't accessible.
'Intranet.ContractsControlDataContext' does not contain a definition for 'ExecuteMethodCall' and no extension method 'ExecuteMethodCall' accepting a first argument of type 'Intranet.ContractsControlDataContext' could be found (are you missing a using directive or an assembly reference?)
Maybe I'm missing something incredibly stupid?
SOLVED
I think perhaps Visual Studio struggled here, but I've relied entirely on auto-generated code. When right clicking on the database modeling language design view and hitting "View Code" it automagically creates a partial class for you within a specific namespace, however, this namespace was wrong. If someone could clarify this for me I would be most appreciative.
The .designer.cs file sits in namespace Intranet.Areas.Accounts.Models, however the .cs file (partial class generated for the .designer.cs file by Visual Studio) was in namespace Intranet. Easy to spot for someone more experienced in this area than me.
The real problem now is, who's answer do I mark as correct? Because many of you contributed to finding this issue.
The object DataContext for linq does not have an empty constructor. Since it does not have an empty constructor you must pass one of the items it is excepting to the base.
From the MetaData for the DataContext.
// Summary:
// Initializes a new instance of the System.Data.Linq.DataContext class by referencing
// the connection used by the .NET Framework.
//
// Parameters:
// connection:
// The connection used by the .NET Framework.
public DataContext(IDbConnection connection);
//
// Summary:
// Initializes a new instance of the System.Data.Linq.DataContext class by referencing
// a file source.
//
// Parameters:
// fileOrServerOrConnection:
// This argument can be any one of the following: The name of a file where a
// SQL Server Express database resides. The name of a server where a database
// is present. In this case the provider uses the default database for a user.
// A complete connection string. LINQ to SQL just passes the string to the
// provider without modification.
public DataContext(string fileOrServerOrConnection);
//
// Summary:
// Initializes a new instance of the System.Data.Linq.DataContext class by referencing
// a connection and a mapping source.
//
// Parameters:
// connection:
// The connection used by the .NET Framework.
//
// mapping:
// The System.Data.Linq.Mapping.MappingSource.
public DataContext(IDbConnection connection, MappingSource mapping);
//
// Summary:
// Initializes a new instance of the System.Data.Linq.DataContext class by referencing
// a file source and a mapping source.
//
// Parameters:
// fileOrServerOrConnection:
// This argument can be any one of the following: The name of a file where a
// SQL Server Express database resides. The name of a server where a database
// is present. In this case the provider uses the default database for a user.
// A complete connection string. LINQ to SQL just passes the string to the
// provider without modification.
//
// mapping:
// The System.Data.Linq.Mapping.MappingSource.
public DataContext(string fileOrServerOrConnection, MappingSource mapping);
Something as simple as this would work. Any class that inherits from the DataConext must pass to the base constructor at least one of the types it is excepting.
public class SomeClass : System.Data.Linq.DataContext
{
public SomeClass(string connectionString)
:base(connectionString)
{
}
}
I'm assuming that the namespace and (data-context) type name are correct... double check that first.
It sounds to me like the codegen has failed, and so you only have your half of the data-context (not the half that the IDE is meant to provide). There is a known bug in LINQ-to-SQL where this can fail if (as in your case) the using declarations are above the namespace. No, I am not joking. Try changing the code:
namespace IntranetMvcAreas
{
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Reflection;
using System.Text;
using IntranetMvcAreas.Areas.Accounts.Models;
// the rest of your code
Now go into the designer, tweak something (for example, change the name of a property and change it back again) and hit save (this forces the codegen). Now see if it works.
David Basarab's answer is correct and should be marked as the answer.
Your class is not providing any constructor, so a default constructor is provided. Default constructors for derived classes can only be provided if the base class has a parameterless constructor. However, the DataContext class which is your base class in this example does not provide a parameterless constructor. This explains the error message the compiler returned to you.
Edit:
Example:
class A {
public A(string s) {
}
}
class B : A {
}
An attempt to compile that returns an error in class B:
'A' does not contain a constructor that takes '0' arguments
The behavior of the generator regarding constructors is controlled, to some extent, by the Connection properties of the DBML. If Application Settings is True, and there is a settings property name, it will generate a constructor that reads the connection string from the Application Settings of the assembly.
If there is a connection string, it will generate a constructor with a hardcoded connection string in the .designer.cs file.
If there is neither, it will not generate a constructor without a connection string parameter, and you can safely provide a parameter-less constructor in a partial class without causing a conflict.
These settings changes don't survive a round trip of the schema from the database, but I simply clear the connection settings from the properties after making changes and before I save the DBML.
#Sander: I think you were on the right track. Instead of using a partial class and implementing the function for the sproc I instead followed this blog and used the *.designer.cs file for implementing it. Whilst I'm still experiencing problems with invalid type casting it did get rid of the original problem.