I want to embed my entity datamodel to my class library that contains methods of database operations. (like MyEntityDatamodel.dll) Simply when i add my entity framework embedded class library to my windows forms application, i am able to use it's methods (insert update delete).
Currently i am able to running methods from dll but the problem is i must add EntityFramework.dll and EntityFramework.SqlServer.dll to my forms application as reference. Otherwise it doesn't work. I want to add only MyEntityDatamodel.dll.
Sample code of my entity datamodel class:
public class MyClass
{
public string classParameter { get; set; }
public void InsertMethod(MyClass parameter)
{
var dbContext = new MyEntities();
InsertOperations...
}
public void UpdateMethod(MyClass parameter)
{
var dbContext = new MyEntities();
UpdateOperations...
}
}
How I am using;
using MyClass;
MyClass myClass = new MyClass();
myClass.classParameter = "example";
myClass.InsertMethod(myClass);
Thanks in advance.
Two things to note...
First, directly to your question - you can't. You may try some sophisticated techniques like dynamically loading the assemblies your program needs, but this changes almost nothing except that you do not have them listed in your project references. They still need to be somewhere where the loader can find them. And more important they must always be deployed together with your app. Otherwise it won't work.
Second, DbContext is meant to be used as a Unit-of-Work design pattern, that is it needs to be disposed after the unit has finished whatever it was meant to do. Usualy, the using clause is a very good choice for this, so consider wrapping your code this way:
using (var dbContext = new MyEntities())
{
InsertOperations.../UpdateOperations...
}
After spending hours of on this, finally i found a solution. I am able to inject connection string to my dbcontext before my dbcontext object was created.
First, i changed my dbcontext's constructor method. I added string parameter for connection string instead reading from the web.config.
I changed this;
public partial class ExampleEntities : DbContext
{
public ExampleEntities()
: base("name=ExampleEntities")
{
}
}
To this;
public partial class ExampleEntities : DbContext
{
public ExampleEntities(string connStr) : base(connStr)
{
}
}
Then i added a method for Generating connection string to my code;
public static string CreateConnStr(string dataSource, string instanceName, string userName, string password)
{
string connectionString = new System.Data.Entity.Core.EntityClient.EntityConnectionStringBuilder
{
Metadata = "res://*/ExampleModel.csdl|res://*/ExampleModel.ssdl|res://*/ExampleModel.msl",
Provider = "System.Data.SqlClient",
ProviderConnectionString = new System.Data.SqlClient.SqlConnectionStringBuilder
{
InitialCatalog = instanceName,
DataSource = dataSource,
IntegratedSecurity = false,
UserID = userName,
Password = password,
}.ConnectionString
}.ConnectionString;
return connectionString;
}
Now i am simply use my dbcontext shown as below;
var dbContext = new ExampleEntities(CreateConnStr("localhost\\SQLEXPRESS","ExampleDataBase","UserName", "Password"));
Hope it helps.
P.s.: Sorry for bad grammar. :)
Related
I have the following problem: My Program uses an .EDMX model generated from a SQL database with EF on the .NET framework.
When I work on the program at home from my desktop PC, I use Windows authentication. When I open the program at work, it is necessary to build a new .EDMX model with new PC name and standard SQL Server authentication.
How is it possible to change the connection string with a login window for example where I can chose user name, server name and DB name. MS Documentation is not a Help
Thanks for your help
First, you can define the helper to generate a runtime connection string for EDMX. you can find more options for your connection here
public static class Helper
{
public static string GenerateConnectionString(string server, string database, string username, string password)
{
return $"data source={server};initial catalog={database};persist security info=True;user id={username};password={password};" +
$"MultipleActiveResultSets=True;App=EntityFramework"";
}
}
Then add contractor for EDMX Model, for example, my Context model name is [banescoEntities]. "for find context you can type EMDXModelName and use F12 for go to DbContext". shown location on pic. But be careful if you rebuild Model Context, Mabe needs to rewrite code
public partial class banescoEntities : DbContext
{
// add this constractor
public banescoEntities(string connectionString)
: base(connectionString)
{
}
public banescoEntities()
: base("name=banescoEntities")
{
}
// other Context items
}
then create instance like :
internal class Program
{
static void Main(string[] args)
{
// generate ConnectionString runtime
var cs = Helper.GenerateConnectionString("172.31.4.24\\MSSQLSERVER2019", "banesco", "sa", "blablabal");
banescoEntities bsEntities = new banescoEntities(cs);
var products = bsEntities.Products.ToList();
}
}
I have this action method in MVC which retrieves data from table using a DbContext:
public ActionResult Index()
{
TwitterContext context = new TwitterContext();
List<TwitterUser> Users = context.User.ToList();
return View(Users);
}
It retrieves the values as expected:
But when I use the same code as a service, it retrieves nothing:
I am calling the service method from the client like this
public class TwitterController : Controller
{
// GET: Twitter
public ActionResult Index()
{
TwitterServiceReference.TwitterContractClient client = new TwitterServiceReference.TwitterContractClient("BasicHttpBinding_ITwitterContract");
List<TwitterServiceReference.TwitterUser> user = client.User().ToList();
return View(user);
}
}
I am using the same connection strings in both cases
<add name="TwitterContext"
connectionString="Integrated Security=true;initial Catalog=TwitterDatabase;server=MYNAME-PC\SQLEXPRESS"
providerName="System.Data.SqlClient"/>
I have all the data filled in the table:
Why is the data empty? How to solve this?
UPDATE:
After checking the SQL Server i see that a database is generated with the name
"TwitterService.TwitterContext , the database is autogenerated when i run the app, and the autogenerated tables data is empty, that is why i am getting empty data returned.
i think you forgot to copy your web.config connection string to that app.config.This might be the issue.
Edit:
i mean the project in which your DataContext class resides
The TwitterUser type you are using in MVC is in namespace Twitter.Models but the one you are using in WCF is in namespace TwitterServiceReference. Therefore, EF will treat them like different things. Your TwitterContext is also in different namespaces and as a consequence this is what is happening (from your comment):
I Just checked the SQL Server Management studio, i think the code created database named "TwitterService.TwitterContext" don't know how and with the same tables as DbContext class
And since it is a brand new database, it has no data and therefore in the WCF service you are not getting any records.
You can have different contexts and use the same database by specifying the same connection string as shown below:
public class TwitterContext : DbContext
{
public MovieDBContext()
: base("TwitterContext")
{
}
}
A Better Approach
Create a separate project and put all your DbContext related code in that project. Most people will refer to this as the Data Access Layer. Then use that project in both the MVC application and the WCF services. This way you will share the TwitterUser and other types in both, and possibly more, applications. You may use Repository Pattern to implement this layer.
But what if my MVC application needs additional info?
There will be cases where TwitterUser may not be sufficient for your MVC views and you may need additional properties. In that case, you can create a Model specifically for that view (some people call this view model but I stay away from that since that is a term used in MVVM and it is very different). The class can be designed like this:
public class TwitterUserModel
{
public TwitterUserModel(TwitterUser user)
{
this.TwitterUser = user;
}
public string AnotherPropertyNeededByView {get; set;}
public TwitterUser TwitterUser { get; set; }
}
Or you can use AutoMapper or similar mappers to map from TwitterUser to TwitterUserModel.
After some trials i got the success
i just added this code
public class TwitterContext : DbContext
{
//Added this Code with the connection string
public TwitterContext() : base(#"Integrated Security=true;server=MYNAME-PC\SQLEXPRESS;database=TwitterDatabase")
{
//Disable initializer
Database.SetInitializer<TwitterContext>(null);
}
public DbSet<TwitterUser> User { get; set; }
public DbSet<TwitterUserData> UserData { get; set; }
}
I first tried adding "TwitterContext" as base("TwitterContext") which is the name of my connection string and matches with my DbContext class name which did not work, so i directly added the connection string, the data is retrieved now and is preventing from generating new database.
I am trying to update a shopping cart in my application using a gridview and a SQLDataSource control, I'm trying to set the update command parameters so that I can send values to my SQL Stored proc, but it keeps throwing an exception saying that it can't convert from nvarchar to string.
Given the below code;
ProductsDataSource.UpdateParameters.Add("Description", row.Cells[2].Text);
it seems that this method will not accept anything other than a String as it's second argument, so how can I convert that to other values to pass into my parameters?
I have already tried something like this;
int productID = int.Parse(row.Cells[1].Text);
but since that second parameter HAS to have a string argument, I can't insert it into my DB (Complaining it can't implicitly convert to string!)
you can specify parameter type on markup..
<UpdateParameters >
<asp:Parameter Name="paramName" DbType="String" Type="String" />
and you can set value on code behind.
ProductsDataSource.UpdateParameters["paramName"].DefaultValue = "parameter value";
OR you can use overloads of Add method without markup definition.
SqlDataSource1.UpdateParameters.Add("paramName", DbType.String, "parameter value");
Rather than directly answer why your code isn't working, I thought I'd present an alternative. Even if you fix your error with SqlDataSource, I believe it's bad to continue using in the long run. Using SqlDataSource as a control on your webpage sprinkles database code in your UI layer. This is very messy and we try to avoid that with modern applications. SqlDataSource also encourages magic strings instead of using strongly typed model objects.
A better alternative is to completely ditch SqlDataSource and use ADO.NET directly (perhaps through some micro-ORM such as Dapper). We can also move this logic into its own class, and follow the repository pattern. That class is best placed in a separate class library that your application then references, then you can reuse the class from other applications. I personally often have a console application so that I can test bits of my repository without having to go through the website.
Rather than having your website rely directly on this repository class though, we often work through an interface. This keeps our website from needing to directly depend on how the database logic is implemented. Often this is coupled with Dependency Injection, but that's a bit too big a subject for this post. I highly recommend you check out this excellent video about Dependency Injection.
So, here's our interface:
public interface IProductRepository
{
Product GetProductById(int id);
void UpdateProduct(Product product);
List<Product> GetAllProducts();
}
Now for the actual implementation:
public class SqlServerProductRepository: IProductRepository
{
private readonly string _connectionString;
public ProductRepository(string connectionString)
{
_connectionString = connectionString;
}
public Product GetProductById(int id)
{
using(var connection = new SqlConnection(_connectionString))
{
//QuerySingle is an extension method from Dapper
return connection.QuerySingle<Product>("select Name, Description, Id from Products where Id = #Id", new {Id = id});
}
}
public void UpdateProduct(Product product)
{
using(var connection = new SqlConnection(_connectionString))
{
//Execute is an extension method from Dapper
connection.Execute("update Products set Name = #Name, Description = #Description where Id = #Id", product);
}
}
public List<Product> GetAllProducts()
{
using(var connection = new SqlConnection(_connectionString))
{
//Query is an extension method from Dapper
//You'd likely want to implement filters/paging etc in a real world app
return connection.Query<Product>("select Name, Description, Id from Products").AsList();
}
}
}
You'll need a model class if you don't have an existing one:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
Now the code in your website becomes much simpler:
//you can remove the direct reference to SqlServerProductRepository
//via Dependency Injection, not shown here
IProductRepository productRepository = new SqlServerProductRepository(connectionString);
var product = productRepository.GetProductById(1);
product.Description = "Updated Description";
productRepository.UpdateProduct(product);
ProductsGridView.DataSource = productRepository.GetAllProducts();
ProductsGridView.DataBind();
There's other ways you could go about implementing your repository, such as having it batch changes until you call SaveChanges etc, but this is a basic implementation.
Another advantage of abstracting your database interaction behind an interface is that you can try out different implementations without needing to change your entire website. Want to try Entity Framework? Create a new EntityFrameworkProductRepository that implements IProductRepository. What if you want to switch databases entirely? SqlLite is free and lightweight and suitable for small apps. Create a new SqlLiteProductRepository.
Try
string productID = (string)(row.Cells[1].Text)
I have a database setup using 'master/slave replication'. I have one master and (at least) one slave, possibly ℕ slaves. For simplicity from here on I'll talk about one master, one slave because determining which slave to use includes some business-logic not relevant to the actual problem at hand.
Here's a schematic of the setup (with ℕ slaves):
In the application (currently using Dapper) I have the following, simplified, code:
abstract class BaseRepo
{
private readonly string _readconn;
private readonly string _writeconn;
public BaseRepo(string readConnection, string writeConnection)
{
_readconn = readConnection; //Actually IEnumerable<string> for ℕ slaves
_writeconn = writeConnection;
}
private SqlConnection GetOpenConnection(string cnstring)
{
var c = new SqlConnection(cnstring);
c.Open();
return c;
}
public SqlConnection GetOpenReadConnection()
{
return this.GetOpenConnection(_readconn);
// Actually we use some business-logic to determine *which* of the slaves to use
}
public SqlConnection GetOpenWriteConnection()
{
return this.GetOpenConnection(_writeconn);
}
}
class CustomerRepo : BaseRepo
{
// ...ctor left out for brevity...
// "Read" functions use the "read" connection
public IEnumerable<Customer> ListCustomers()
{
using (var c = this.GetOpenReadConnection())
{
return c.Query<Customer>("select * from customers order by name");
}
}
// "Write" functions use the "write" connection
public void UpdateCustomer(Customer cust)
{
using (var c = this.GetOpenWriteConnection())
{
c.Execute("update customers set name = #name where id = #id", cust);
}
}
}
My question is; suppose I want to use Entity Framework ("code first", should that be relevant) instead of Dapper; how would I best go about achieving the same concept; inserts/updates/deletes are executed against the "master" database and selects are executed against a slave (or any of the slaves). Does EF support this scenario at all? What would I need to do to make this work?
Additional info: I already use 'read-only' and 'write-only' users at the SQL Server level as a 'last line of defence' to prevent any mistakes in the DAL. What I'm looking for is a method of limiting my DAL to avoid having to catch SQL Server exceptions because of 'not allowed' actions and having to go to the (incorrect) SQL server in the first place before finding out the desired action is not allowed. I could use the same approach as I do now; instantiate/use the correct DbContext in the method itself (listcustomers/updatecustomer in the above example). I get that. But that would mean I'd have to create a 'wrapper' function for each "CRUD" action on each "entity" which was kind of why I was moving from dapper to EF in the first place; simply expose a DBSet and have EF take care of the changetracking/SQL queries etc. and now, hopefully, also figure out which connectionstring to use for each action.
As proposed by others, create a read/write context by default and then create a readonly one inheriting from it.
Also be sure to implement in a partial class a constructor accepting another configuration if you wish too.
public partial class CustomerEntities : DbContext
{
protected CustomerEntities(string nameOrConnectionString):base(nameOrConnectionString)
{
}
}
public class ReadonlyCustomerEntities : CustomerEntities
{
public ReadonlyCustomerEntities ()
: base("name=ReadonlyCustomerEntities")
{
}
public override int SaveChanges()
{
// Throw if they try to call this
throw new InvalidOperationException("This context is read-only.");
}
}
I have a connectionString as follows:
<add name="EDentalCADBContext" connectionString="data source=*.*.*.*\SQLEXPRESS;Initial Catalog=*******;User id=*****;Password=******;" providerName="System.Data.SqlClient" />
I created the same ConnectionString dynamically, but I am unable to set the name of the ConnectionString such as name="EDentalCADBContext" in above.
How can I set the name dynamically?
Thanks
I agree with #Dai above. If you are creating it dynamically in the code, then what's the point of putting into a configuration wrapper. Why not just use it directly? The point of that entire interface is really to read stuff out from the common .config files.
Nevertheless, it is possible to do. When you create a ConnectStringSettings instance, you pass 'name' and 'connectionString' in the constructor: https://msdn.microsoft.com/en-us/library/wd8h82yf(v=vs.110).aspx
EDentalCADBContext context = new EDentalCADBContext();
public ActionResult Masters(int? pageNumber, string tableName)
{
EstablishConnection(); // This creates the connection with DB dynamically.
int? page = 1;
ViewData["MenuItems"] = MenuItem.ActivateMenu("Masters");
ViewData["Users"] = context.Users.ToList();
}
public class EDentalCADBContext : DbContext
{
// public EDentalCADBContext() : base("EDentalCADBContext") { }
public DbSet<NotificationItem> NotificationItems { get; set; }
public DbSet<User> Users { get; set; }
It is expecting EDentalCADBContext name of the connection string. Why should it match?
I'm stabbing in the dark without knowing specifically how the rest of your code works. But you asked me to try, so here's my best go...
If you have your own custom class inheriting from DbContext then you have to implement your own constructor to take in the connection string. You don't get to inherit non-default constructors. So I've modified your class below to have a constructor to take in the string:
public class EDentalCADBContext : DbContext {
public DbSet<NotificationItem> NotificationItems { get; set; }
public DbSet<User> Users { get; set; }
public EDentalCADBContext(string connectionNameOrString) :
base(connectionStringOrName) {
}
}
You would instantiate like this:
EDentalCADBContext context = new EDentalCADBContext("literal conn string");
// Perhaps comes from sqlBuilder.ConnectionString per your comments?
In your comment, you wrote
I dont want to use ConnectionString from web.config. I created it dynamically. So I need to set the name to match it with DBContext
The solution is not to match the "magic string" that the DbContext expects (I imagine this was specified when you used the EF wizard in Visual Studio) but instead use the DbContext constructor that accepts a raw connection string:
String connectionString = "server=dbserver.local;Integrated Security=SSPI;...";
using( DbContext db = new DbContext( connectionString ) ) {
// do stuff
}
public EDentalCADBContext(string con = null)
: base(string.IsNullOrEmpty(con) ? "name=EDentalCADBContext" : con)
{
}
In your EDentalCADBContext context class.
Then when you instance this EDentalCADBContext you passing a Connection string as parameter.
Ex. EDentalCADBContext db = new EDentalCADBContext(Connection string);