Objective:
Use EF to enter in data from a POST request from an API. The POST request will contain an "ID", that will map the connection string to an enum, which will have the same name connection string name as it has in the Web.config. Create the "base" context object and add the object to the appropriate table.
Note:
I know I can do this using SqlCommand, but I wanted to take a crack at it using entity framework instead, but I hit a wall.
I've used EF for years, but I wanted to make this POST method as global as I can get it. This API will accept numerous requests from numerous different web sites, but all will use the same model. Each websites POST will go into a different database (that's how they are requesting it).
The problem that I foresee is, each "entity" knows what tables it contains. So when one types context.TABLE.Add(object), EF understands that you want to put this "Car" object in the "Car" table (obviously).
Can this be done using a "global" entity???
public class DbConnectionNames
{
public enum DbConnectionStringNames
{
SocHopeHcpEntities = 1, // "1" is passed into the POST to map
XXXXEntities = 2,
......
}
}
<add name="SocHopeHcpEntities" connectionString=".........." />
<add name="XXXXEntities" connectionString=".........." />
.....
var professional = new Professional
{
....
....
};
string connStringContext = Enum.GetName(typeof(DbConnectionNames.DbConnectionStringNames), model.FormId).ToString();
string connectionString = ConfigurationManager.ConnectionStrings[connStringContext].ConnectionString;
using (var context = new ObjectContext(connectionString))
{
context.Professionals.Add(professional); // obviously this doesn't work
context.SaveChanges();
}
EDIT:
My EF is NOT using POCO, but is already based off a DB to begin with. There could be XX number of different databases, all holding the same similar table. I already have a YYYEntities.Context.cs file auto-generated that inherits from DbContext:
public partial class SocHopeHcpEntities : DbContext
{
public SocHopeHcpEntities()
: base("name=SocHopeHcpEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<AreasOfWork> AreasOfWorks { get; set; }
public virtual DbSet<Professional> Professionals { get; set; }
}
You still need a context that understands what a Professional is. For example:
public class ProfessionalContext : DbContext
{
public ProfessionalContext(string connectionString)
: base(connectionString)
{
//This line is optional but it prevents initialising the database
//every time you connect to a new database
Database.SetInitializer<ProfessionalContext>(null);
}
public DbSet<Professional> Professionals { get; set; }
}
And use it like this:
using (var context = new ProfessionalContext(connectionString))
{
context.Professionals.Add(professional);
context.SaveChanges();
}
DavidG's answer showed you how to pass a connection string to a strongly typed context that knows the entity types you are dealing with. That context inherits from DbContext which you can use directly as illustrated below.
It is worth noting the generic way that does not involve a context 'object' that is specific to your database.
For example see how ObjectContext is used here:
System.Data.Objects.ObjectContext oc = new System.Data.Objects.ObjectContext("connection string");
oc.AddObject("ProfessionalsTable", professional);
Another example is using DbContext:
System.Data.Entity.DbContext dbc = new DbContext("");
dbc.Set(typeof(Professional)).Add(professional);
The generic approach is better if you also do not know which table you want to insert to, so you can also make the object that you want to insert dynamic.
Related
Being new to ASP .NET I've been researching on entity modeling approaches and it seems ORM using Entity Framework following Code First is the best approach for me.
By following this tutorial, I have gotten the impression that, for each entity, you need to create a connectionstring (correct me if I'm wrong) and this fact confuses me when it comes to relational data, as, per this example, the database itself seems to cover just one entity. So how is relational data handled in the EF.
PS: For unification purpose, please use entities Movies, Customers and the relational table named under the proper naming conventions.
You create a connection string per DbContext. Here is the class that defines the DBContext:
public class MovieDBContext : DbContext
{
public DbSet<Movie> Movies { get; set; }
}
To add more tables to this context, add more lines like this (for examples, public DbSet<Customer> Customers { get; set; }.
public class MovieDBContext : DbContext
{
public DbSet<Movie> Movies { get; set; }
public DbSet<Ticket> Tickets { get; set; }
}
Accessing these from a context:
public class TicketController {
private MovieDBContext db = new MovieDBContext ();
public ActionResult Index(int movieId) {
var listOfTickets = db.Tickets.Where(t=>t.MovieId == movieId).ToList();
var parentMovie = db.Movie.Where(m=>m.Id == movieId).Single();
...
}
}
Connection String: Contains initialization information that is passed as a parameter from a data provider to a data source.
You need a new connection string each time you are connecting to something different (You can have two different DBContexts using the same ConnectionString), in this tutorial although the Data Source of both connection strings is the same, the AttachDbFileName is different.
When each DbContext is initialized, it will use one of those connection strings. In this tutorial, the first connection string (Default Connection) is used for membership (user accounts and such) and the other connection string is used for your MovieDBContext, and will contain Movies and other things as you progress in the tutorial.
It's also possible to have them both in the same database.
I'm prototyping an ASP.NET Web API that needs to talk to several databases which are almost identical. Each of our customers have their own instance of our database structure, but some are specialized to integrate with other systems they have. So for example in one database the Client table might have the column AbcID to reference a table in another system, but other databases won't have this column. Other than that the two tables are identical in name and columns. The columns can also have different lengths, varchar(50) instead of varchar(40) for example. And in some databases there can be one extra table. I have focused on solving the different columns problem first.
I was hoping to use an ORM to handle the data access layer of the API, and right now I'm experimenting with Entity framework. I already solved how to dynamically connect to the different databases from an API-call, but right now they have to be completely identical in structure.
I have tried to set up double .edmx models with a Database-first approach but this causes conflicting class names between the models. So instead I tried Code-first and come up with this (which isn't working).
DbContext extension:
In the constructor I check which database is being accessed and if it is one of the special ones I flag it for the model configuration.
public partial class MK_DatabaseEntities : DbContext
{
private string _dbType = "dbTypeDefault";
public DbSet<Client> Client { get; set; }
public DbSet<Resource> Resource { get; set; }
public MK_DatabaseEntities(string _companycode)
: base(GetConnectionString(_companycode))
{
if(_companycode == "Foo")
this._dbType = "dbType1";
}
// Add model configurations
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Configurations
.Add(new ClientConfiguration(_dbType))
.Add(new ResourceConfiguration());
}
public static string GetConnectionString(string _companycode)
{
string _dbName = "MK_" + _companycode;
// Start out by creating the SQL Server connection string
SqlConnectionStringBuilder sqlBuilder = new SqlConnectionStringBuilder();
sqlBuilder.DataSource = Properties.Settings.Default.ServerName;
sqlBuilder.UserID = Properties.Settings.Default.ServerUserName;
sqlBuilder.Password = Properties.Settings.Default.ServerPassword;
// The name of the database on the server
sqlBuilder.InitialCatalog = _dbName;
sqlBuilder.IntegratedSecurity = false;
sqlBuilder.ApplicationName = "EntityFramework";
sqlBuilder.MultipleActiveResultSets = true;
string sbstr = sqlBuilder.ToString();
return sbstr;
}
}
ClientConfiguration:
In the configuration for Client I check the flag before mapping properties to database columns. This however does not seem to work.
public class ClientConfiguration : EntityTypeConfiguration<Client>
{
public ClientConfiguration(string _dbType)
{
HasKey(k => k.Id);
Property(p => p.Id)
.HasColumnName("ID")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
if (_dbType == "dbType1")
{
Property(p => p.AbcId).HasColumnName("AbcID");
}
Property(p => p.FirstName).HasColumnName("FirstName");
Property(p => p.LastName).HasColumnName("LastName");
}
}
Client class:
This is how my Client class looks like, nothing weird here.
public class Client : IIdentifiable
{
public int Id { get; set; }
public string AbcId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public interface IIdentifiable
{
int Id { get; }
}
Back-up solution is to use raw SQL queries to deal with the offending tables and ORM for the rest, but it would be awesome if there is some way to do this that I have not thought of. Right now I'm trying Entity framework, but I am not opposed to trying some other ORM if that one can do it better.
Using Code First supports this scenario:
1) Common entities for both models:
public class Table1
{
public int Id { get; set; }
public string Name { get; set; }
}
2) Base version of table 2
public class Table2A
{
public int Id { get; set; }
public int Name2 { get; set; }
public Table1 Table1 { get; set; }
}
3) "Extended" version of table 2, inherits version A, and adds an extra column
public class Table2B : Table2A
{
public int Fk { get; set; }
}
4) Base context, including only the common entities. Note that there is a constructor which accepts a connection string, so there is no parameterless constructor. This forces inheriting contexts to provide their particular connection string.
public class CommonDbContext : DbContext
{
public CommonDbContext(string connectionString)
:base(connectionString)
{
}
public IDbSet<Table1> Tables1 { get; set; }
}
5) The context A, inherits the common context, adds the Table2A, and ignores the Table2B
public class DbContextA : CommonDbContext
{
public DbContextA() : base("SimilarA") { } // connection for A
public IDbSet<Table2A> Tables2A { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Ignore<Table2B>(); // Ignore Table B
}
}
The context B, inherits the common, and includes the Table2B
public class DbContextB: CommonDbContext
{
public DbContextB() :base("SimilarB") { } // Connection for B
public IDbSet Tables2B { get; set; }
}
With this setup, you can instance either DbContextA or DbContextB. One advantage is that both inherit CommonDbContext, so you can use a variable of this base class to access the common entities, no matter if the concrete implementation is version A or B. You only need to change to the concrete type to access the specific entities of A or B (Table2A or Table2Bin this sample).
You can use a factory, or DI or whatever to get the required context depending on the DB. For example this could be your factory implementation:
public class CommonDbContextFactory
{
public static CommonDbContext GetDbContext(string contextVersion)
{
switch (contextVersion)
{
case "A":
return new DbContextA();
case "B":
return new DbContextB();
default:
throw new ArgumentException("Missing DbContext", "contextVersion");
}
}
}
NOTE: this is working sample code. You can of course adapt it to your particular case. I wanted to keep it simple to show how it works. For your case you'll probably need to change the factory implementation, and expose the connection string in A and B context constructors, and provide it in the factory method
Handling the different classes of your entities
The easiest way to handle the different entities of each DbContext is to use polymorphism, and or generics.
If you use polymorphism you need to implement methods which use the type of the base class (as parameter and as return type). This parameters and vars will hold entities either of the base or of the derived class (Table2A or Table2B). In this case, each context will receive an entity of the right type, and it will work directly without trouble.
The problem is when your app is multilayered, uses services or is a web app. In this case when you use the base class the polymorphic behavior can be lost, and you'll need to handle the entities of the base class. (For example if you let the user edit an entity of derived class in a web app form, the form can only take care of the properties of the base class, and when it's posted back, the properties of the derived class will be lost) In this case, you need to handle it intelligently (see note below):
For reading purposes, if you have a Table2B, you have a direct casting to Table2A. You can implement functionality for Table2A and directly used it. I.e. you can return collections or individual values of the base class (in many cases implicit casting will be enough). No more worries.
For inserting/updating, you have to take extra steps, but it's not too difficult. You need to implement methods that receive/return Table2A parameters in your contexts, or in another layer, depending on your architecture. For example, you can make the base context abstract and define virtual methods for this. (See example below). Then you need to make the right implementation for each particular case.
if you receive a Table2A but need to insert it in Table2B, simply map entity A into entity B with AutoMapper or ValueInjecter and fill the remaining properties with default values (beware of AutoMapper and EF dynamic proxies: it won't work).
if you receive a Table2A and need to update a Table2B, simply read the existing entity from the DB and repeat the mapping procedure (ValueInjecter will be less troublesome than AutoMapper also for this case).
This is a very simple example of what can be done, but you need to adapt it to your particular case:
Inside CommonDbContext class, declare virtual methods for the base type, like this:
public virtual Table2A GetTable2AById(int id);
public virtual void InsertTable2A(Table2A table);
You can also use generic interfaces/ methods, instead of abstract class / virtual methods, like this:
public T GetTable2AById<T>(int id)
{
// The implementation
}
In this case you should add the necessary constraints to the T type, like where T: Table2A or the ones you need (class new()).
NOTE It's not exact to say that the polymorphism is lost in this cases, because you can really make polymorphic Web Services with WCF, or Web API, adapt your UI to the real class of your entity (with templates for each case) and so on. That depends on what you need or want to achieve.
Been there, done that.
In all seriousness: dump EF in this specific case; it will bring a lot of pain and suffering for no benefit.
What you'll eventually end up doing (putting my Fortuneteller Hat on) is you'll rip out all the EF-based code, create an abstract object model and then write a series of backends that will map all the various database structures back and forth to said clean abstract object model. And you'll be either using raw SQL or something lightweight like Dapper or BLToolkit.
This is a very weird architecture. Please bear with me.
We have an existing tiered application (data, logic/service, client).
The latest requirement is that the service layer should access two data sources!!!! (no other way around)
These two data sources have the same DB schema.
As with most tiered architectures, we have read and write methods like:
IEnumerable<Product> GetAllProducts(),
Product GetProductById(ProductKey id),
IEnumerable<Product> FindProductsByName(string name)
the product DTOs are:
class Product
{
public ProductKey Key { get; set;}
...
}
class ProductKey
{
public long ID { get; }
}
We narrowed it down to two possible solutions:
Alternative 1:
Add a parameter into the read methods so that the service knows what DB to use like so:
Product GetProductById(ProductKey id, DataSource dataSource)
DataSource is an enumeration.
Alternative 2 (my solution):
Add the DataSource property to the key classes. this will be set by Entity Framework when the object is retrieved. Also, this will not be persisted into the db.
class ProductKey
{
public long ID { get; }
public DataSource Source { get; } //enum
}
The advantage is that the change will have minimal impact to the client.
However, people dont like this solution because
the DataSource doesn't add business value. (My response is that
the ID doesn't add business value either. Its a surrogate key. Its
purpose is for tracking the persistence)
The children in the object graph will also contain DataSource which is redundant
Which solution is more sound? Do you have other alternatives?
Note: these services are used everywhere.
What I would suggest is door number 3:
[||||||||||||||]
[|||||||||s! ]
[||||nerics! ]
[ Generics! ]
I use a "dynamic repository" (or at least that is what I have called it). It is setup to be able to connect to any datacontext or dbset while still being in the same using block (i.e. without re-instantiation).
Here is a snippet of how I use it:
using (var dr = new DynamicRepo())
{
dr.Add<House>(model.House);
foreach (var rs in model.Rooms)
{
rs.HouseId = model.House.HouseId;
dr.Add<Room>(rs);
}
}
This uses the "default" dbcontext that is defined. Each one must be defined in the repository, but not instantiated. Here is the constructor I use:
public DynamicRepo(bool Main = true, bool Archive = false)
{
if (Main)
{
this.context = new MainDbContext();
}
if (Archive)
{
this.context = new ArchiveDbContext();
}
}
This is a simplified version where there are only two contexts. A more in depth selection method can be implemented to choose which context to use.
And then once initialized, here would be how the Add works:
public void Add<T>(T te) where T : class
{
DbSet<T> dbSet = context.Set<T>();
dbSet.Add(te);
context.SaveChanges();
}
A nice advantage of this is that there is only one spot to maintain the code for interacting with the database. All the other logic can be abstracted away into different classes. It definitely saved me a lot of time to use a generic repository in this fashion - even if I spent some time modifying it at first.
I hope I didn't misunderstand what you were looking for, but if you are trying to have one repository for multiple data sources, I believe this is a good approach.
I am using RIA Service in our Silverlight application. Database entities are not directly exposed to a client but I have a set of POCO classes for it. Then in CRUD methods for these POCO classes they are converted to database entities and saved to database.
The problem arises on the server side when client creates 2 new POCO entities which are related. Insert method is called on the server for each POCO entity separately and I may create corresponding new database entities there and add them to object context. But I see no way to add relation between these created database entities. Is there a solution for that?
For example, I have these 2 POCO entities (simplified):
[DataContract(IsReference = true)]
public partial class Process
{
[DataMember]
[Key]
public string Name
{
get; set;
}
[DataMember]
public long StepId
{
get; set;
}
[DataMember]
[Association("StepProcess", "StepId", "Id", IsForeignKey=true)]
public Step Step
{
get; set;
}
}
[DataContract(IsReference = true)]
public partial class Step
{
[DataMember]
[Key]
public long Id
{
get; set;
}
[DataMember]
public string Name
{
get; set;
}
}
And I have these 2 Insert methods in my domain service class:
public void InsertProcess(Process process)
{
var dbProcess = new DBProcess();
dbProcess.Name = process.Name;
//dbProcess.StepId = process.StepId; Cannot do that!
this.ObjectContext.AddToDBProcess(dbProcess);
}
public void InsertStep(Step step)
{
var dbStep = new DBStep();
dbStep.Name = step.Name;
this.ObjectContext.AddToDBSteps(dbStep);
this.ChangeSet.Associate<Step, DBStep>
(step, dbStep, (dto, entity) =>
{
dto.Id = entity.Id;
});
}
Client adds a new Process, then creates and adds a new Step to it and then calls SubmitChanges(). Process.StepId is not filled with a correct value as there is no correct Step.Id for the newly created step yet, so I cannot just copy this value to database entity.
So the question is how to recreate relations between newly created database entities the same as they are in newly created DTOs?
I know about Composition attribute but it is not suitable for us. Both Process and Step are independent entities (i.e. steps may exist without a process).
There are two ways to solve this:
Have each call return the primary key for the item after it is created, then you can store the resulting PKey in the other POCO to call the second service.
Create a Service method that takes both POCOs as parameters and does the work of relating them for you.
Thanks, although both these suggestions are valid but they are also applicable only for simple and small object hierarchies, not my case. I end up using approach similar to this. I.e. I have a POCO to database objects map. If both Process and Step are new, in InsertProcess method process.Step navigation property is filled with this new step (otherwise StepId can be used as it referenced to existing step). So if this process.Step is in the map I just fill corresponding navigation property in DBProcess, otherwise I create new instance of DBStep, put it to the map and then set it to DBProcess.Step navigation property. This new empty DBStep will be filled in InsertStep method later.
I need to store an instance of an complex datatype into a relational database.
Is there a way to do this without moddeling the database structure first, like it is done in ado.net? The database (or table) structure should be created from the class structure. The class has some properties like ints, strings or bools but could also have more complex ones.
I am grateful for every helping advice...
I want to do this in c#...
Update:
Thank you for your replys. I tried "code first" of EF 4 (Thank you very much Ramesh) and got my objects into the database. But now I am having a problem to get the data out of the db back to the instance of an object. My classes look like that:
class Foo {
public int id { get; set; }
public string Woot { get; set; }
}
class Bar {
public int id { get; set; }
public string name { get; set; }
public ICollection<Foo> FooList { get; set; }
}
So, as I said i can create instances of these classes and write them to the db. But when I try to create new instances from the db data, just the int and the string of the "Bar"type is recovered but the Collection of Foos is emtpy. My db context looks like this:
class DBstructure: DbContext
{
public DbSet<Foo> Foos { get; set; }
public DbSet<Bar> Bars { get; set; }
}
any Idea?
Yes thats possible, you have to use Entity framework 4 for this. the approach is called "Code First".
you can read lot about this here in ScottGu's post Code-First Development with Entity Framework 4
The best is to use an ORM tool that can build the database for you from your domain model (classes).
Look at NHibernate, Entity Framework to mention a few.
I got what I wanted using the Entity Framework 4 with "code first". To write Objects from the classes I posted above, into the databse you just have to create the objects regulary:
Foo foo = new Foo();
foo.Woot = "foo1";
Foo foo2 = new Foo();
foo2.Woot = "foo2";
Bar bar = new Bar();
bar.name = "bar1";
bar.Whee = new List<Foo>();
bar.Whee.Add(foo);
bar.Whee.Add(foo2);
Then create the DbContext object, add the other objects to this context and call saveChanges() method:
DBstructure dbconn = new DBstructure();
dbconn.Database.Connection.ConnectionString = connectionString //depends on your DB
dbconn.Bars.Add(bar);
dbconn.Database.Connection.Open();
dbconn.SaveChanges();
dbconn.Database.Connection.Close();
That creates the new database with the tables "Foos" and "Bars" and another link table to manage the relations between the entitys.
Creating objects from the database information is also quite easy. Just connect to the database via the DbContext as it was done above. Create your objects and set them to the db data:
DBstructure dbconn = new DBstructure();
dbconn.Database.Connection.ConnectionString = connString;
dbconn.Foos.ToList(); //seems that you have to do that
dbconn.Bars.ToList<Bar>();
Bar barFromDB = new Bar();
barFromDB = dbconn.Bars.First<Bar>();
That works fine in my application. Sorry for the strange class and variable names. I just copied the code from my test application. Hope that helps someone...
Grüßung Jucker!