Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating model on the fly

Is it possible to create the entire model on the fly (Database First) approach using entity framework each time a connection string is passed in?

I tried the following:

MetaModel model = new MetaModel();
model.RegisterContext(() => new Model1(connectionString),
new ContextConfiguration()
 {
  ScaffoldAllTables = true
 });

but it keeps throwing me an error

An unhandled exception of type 'System.ArgumentException' occurred in System.Web.DynamicData.dll

Additional information: The context type 'DbContext.Model1' is not supported.

More Info:

I have all the required tables in the database which I don't have control over and if I need any new tables or columns inside old tables then the db guys run the appropriate scripts for me.

I'm trying to create a generic DLL using EF which I can use on various apps 6 including winforms and mvc web apps. I'm trying to figure out what us the best possible way to go about this.

Can I mix codefirst and databasefirst together?

Wouldn't codefirst generate tables for me which I already have?

Each of my app consist of 1 winforms and 1 web app and they share the datasource as e.g.

Test 1 Windows App & Test 1 MVC App = DB 1

Test 2 Windows App & Test 2 MVC App = DB 2

Test 3 Windows App & Test 3 MVC App = DB 3

So I need to pass in the connectionstring to the DBContext. How would my Entities work?

If any more information is required please let me know.

like image 526
Izzy Avatar asked Jun 21 '16 13:06

Izzy


1 Answers

Technically you can create entity framework models at run-time, at least by creating an assembly on the fly and using apis and attributes that code first works or by creating required xml for model. But you don't need to create model classes at run-time.

In fact creating model classes at run-time is useless, because you create models to work typed both at compile-time and run-time. You create models at because you want to pass a Type1 to a method or write such typed query like .Where(x=>x.SomeFiled == SomeValue).

If you have different instances of application for different customers

If you have different instance of application for different clients, you don't need to do anything specific. Your application has been written ready to use and just use different connection strings in webconfig and appconfig for different customers.

If you have a single instance of application for all customers

In such case, which you have a multi-tenant application, you can simply add an overload to your db context constructor that accepts connection string as input. Then when you need to create an instance of your db context, you can use that overload and inject suitable username, password and database name in the connection string based on your tenant detection strategy.

public partial class SampleDbEntities
{
    public SampleDbEntities(string connectionString) : base(connectionString)
    {
    }
}

It's better to put the new overload in a partial class. Then it will not be touched each time when you update the edmx and .tt template of context run.

For example you can create connection string and context this way:

var connectionTemplate =
    @"metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;" +
    @"provider=System.Data.SqlClient;" +
    @"provider connection string=""data source={0};" +
    @"initial catalog={1};" +
    @"persist security info=True;" +
    @"user id={2};" +
    @"password={3};" +  
    @"MultipleActiveResultSets=True;App=EntityFramework""";

string connection = string.Format(connectionTemplate, 
    @"(localdb)\v11.0", @"SampleDB1", @"user1" , @"password1");

var db = new SampleDbEntities(connection);

Or maybe you want windows authentication, then instead of user id and password, use @"integrated security=True;" +.

You can detect different tenants based on different strategies, including:

  • Url of application (domain and sub domain)
  • Query string or Route values
  • Username

You can give the role of creating suitable context to a class which works based on your tenant strategy.

What should I do if a table in database changed?

Just update your edmx model at design-time and rebuild your application and redistribute it. As mentioned above, if a filed added to a table or a new table added to database and you want to write such typed query in application db.Tabe1.Where(x=>x.Field1==value1), then you need to update model from database and rebuilding your application. Regenerating model at run-time doesn't make sense.

How can I increase productivity?

I know your goal is increasing productivity, but generating models at run-time is not what you are looking for. Instead you can create some generic Data Access Layers and generic Business Logic Layers to increase productivity. For example, if you have lot's of entities which need CRUD operations, you can create a EntityBusiness<TContext, TModel> class and have generic void Create(TModel entity), IList<TModel> GetAll(object key), TModel GetByKey(object key), TModel Update(TModel entity), void DeleteByKey(object key), etc. This way you can simply create an instance of EntityBusiness<SampleDbEntities, Product> or inherit from it. The class contains all behavior for a simple CRUD operation and you can enhance it by adding support for some other useful cases like validations. As an example of creating a generic repository and unit of work, take a look at this article in asp.net mvc site.

like image 90
Reza Aghaei Avatar answered Oct 20 '22 00:10

Reza Aghaei