2015-05-01 2 views
18

Я пытаюсь создать динамическую службу odata из таблиц в моих таблицах, которые неизвестны до времени выполнения. Поэтому в начале моего веб-приложения пользователь выбирает базу данных, а в C# я нахожу все таблицы в этой базе данных.Динамическая служба odata в C# из уровня данных времени выполнения

Теперь я хочу создать одинарные точки обслуживания для каждой таблицы в базе данных и использовать их соответственно в своем веб-приложении. Проблема в том, что я не знаю, как это сделать динамически. Существует множество примеров с известными таблицами базы данных времени компиляции, но в этом случае я не буду иметь их до тех пор, пока мой пользователь не будет использовать мое приложение.

Любые помощь или идеи приветствуются.

+0

вы взглянули на http://www.odata.org/blog/restier-a-turn-key-framework-to-build-restful-service/ я получил в той же ситуации, и я ищу решение по этому вопросу. –

+0

Просьба пояснить: Вы говорите о том, что служба OData обнаруживает набор баз данных и таблиц один раз, когда служба инициализируется? Или служба OData должна поддерживать базы данных, которые появляются вверх и вниз в течение всего времени выполнения? – lencharest

ответ

11

Существует пример, который не требует предопределенного класса, доступного здесь: ODataUntypedSample, но для этого требуется предопределенный контроллер.

Я построил на нем еще один пример приложения консоли, чтобы иметь возможность запрашивать любую базу данных SQL-сервера с помощью OData. Я использовал этот пакет nuget для чтения схемы базы данных и данных: DatabaseSchemaReader. Вам понадобится следующий NuGet пакет, чтобы иметь возможность построить его (плюс зависимостей):

  • Microsoft.Owin.Hosting
  • Microsoft.Owin.Host.HttpListener
  • Microsoft.AspNet.WebApi.Owin
  • Microsoft.AspNet.OData

Здесь основная программа немного изменена таким образом он объявляет EDM (для OData) сущностей из таблиц. Я проверил стандартный образец Adventure Works 2014 но он должен работать на любом столе многообещающе:

class Program 
{ 
    private static HttpClient client = new HttpClient(); 
    private static TableControllerSelector selector; 
    private const string ServiceUrl = "http://localhost:12345"; 
    private const string connectionString = @"Server=MYSQLSERVER;Database=AdventureWorks2014;Integrated Security=SSPI"; 

    static void Main(string[] args) 
    { 
     using (WebApp.Start(ServiceUrl, Configuration)) 
     { 
      Console.WriteLine("Server is listening at {0}", ServiceUrl); 

      RunSample(); 

      Console.WriteLine("Press any key to continue . . ."); 
      Console.ReadKey(); 
     } 
    } 

    public static void Configuration(IAppBuilder builder) 
    { 
     HttpConfiguration configuration = new HttpConfiguration(); 

     // create a special dynamic controller selector 
     selector = new TableControllerSelector(configuration); 
     IEdmModel model = TableController.GetEdmModel(connectionString, selector); 
     configuration.Services.Replace(typeof(IHttpControllerSelector), selector); 

     configuration.MapODataServiceRoute("odata", "odata", model); // needs using System.Web.OData.Extensions 
     builder.UseWebApi(configuration); 
    } 

    public static void RunSample() 
    { 
     Console.WriteLine("1. Get Metadata."); 
     GetMetadata(); 

     Console.WriteLine("\n2. Get Entity Set."); 
     using (var dbReader = new DatabaseReader(connectionString, "System.Data.SqlClient")) 
     { 
      foreach (var table in dbReader.AllTables()) 
      { 
       Console.WriteLine("\n 2.1 Get Entity Set '" + table.Name + "'."); 
       GetEntitySet(table.Name); 
      } 
     } 
    } 

    public static void GetMetadata() 
    { 
     HttpResponseMessage response = client.GetAsync(ServiceUrl + "/odata/$metadata").Result; 
     PrintResponse(response); 
    } 

    public static void GetEntitySet(string tableName) 
    { 
     HttpResponseMessage response = client.GetAsync(ServiceUrl + "/odata/" + tableName + "?$filter=Id eq 1").Result; 
     PrintResponse(response); 
    } 

    public static void PrintResponse(HttpResponseMessage response) 
    { 
     response.EnsureSuccessStatusCode(); 
     Console.WriteLine("Response:"); 
     Console.WriteLine(response); 

     if (response.Content != null) 
     { 
      Console.WriteLine(response.Content.ReadAsStringAsync().Result); 
     } 
    } 
} 

И специальные TableController и TableControllerSelector классов, которые позволяют создать модель EDM из любой базы данных SQL Server, а также создавать контроллеры динамически из EDM сущности в этой модели:

public class TableControllerSelector : DefaultHttpControllerSelector 
{ 
    private Dictionary<string, HttpControllerDescriptor> _tables = new Dictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase); 

    public TableControllerSelector(HttpConfiguration configuration) 
     : base(configuration) 
    { 
     Configuration = configuration; 
    } 

    public HttpConfiguration Configuration { get; private set; } 

    public override HttpControllerDescriptor SelectController(HttpRequestMessage request) 
    { 
     string name = GetControllerName(request); 
     if (name != null) // is it a known table name? 
     { 
      HttpControllerDescriptor desc; 
      if (_tables.TryGetValue(name, out desc)) 
       return desc; 
     } 

     return base.SelectController(request); 
    } 

    public void AddTable(string connectionString, DatabaseTable table) 
    { 
     if (connectionString == null) 
      throw new ArgumentNullException("connectionString"); 

     if (table == null) 
      throw new ArgumentNullException("table"); 

     // create a descriptor with extra properties that the controller needs 
     var desc = new HttpControllerDescriptor(Configuration, table.Name, typeof(TableController)); 
     desc.Properties["table"] = table; 
     desc.Properties["connectionString"] = connectionString; 
     _tables[table.Name] = desc; 
    } 
} 

public class TableController : ODataController 
{ 
    // this will be called for standard OData access to collection 
    public EdmEntityObjectCollection Get() 
    { 
     // get Edm type from request 
     ODataPath path = Request.ODataProperties().Path; // ODataProperties() needs using System.Web.OData.Extensions 
     IEdmType edmType = path.EdmType; 

     IEdmCollectionType collectionType = (IEdmCollectionType)edmType; 
     IEdmEntityType entityType = (IEdmEntityType)collectionType.ElementType.Definition; 
     IEdmModel model = Request.ODataProperties().Model; 

     ODataQueryContext queryContext = new ODataQueryContext(model, entityType, path); 
     ODataQueryOptions queryOptions = new ODataQueryOptions(queryContext, Request); 

     // TODO: apply the query option on the IQueryable here. 

     // read all rows from table (could be optimized using query context) 
     var table = (DatabaseTable)ControllerContext.ControllerDescriptor.Properties["table"]; 
     var cnx = (string)ControllerContext.ControllerDescriptor.Properties["connectionString"]; 

     return new EdmEntityObjectCollection(new EdmCollectionTypeReference(collectionType), ReadData(entityType, table, cnx)); 
    } 

    public static IList<IEdmEntityObject> ReadData(IEdmEntityType type, DatabaseTable table, string connectionString) 
    { 
     List<IEdmEntityObject> list = new List<IEdmEntityObject>(); 

     // https://www.nuget.org/packages/DatabaseSchemaReader/ 
     Reader reader = new Reader(table, connectionString, "System.Data.SqlClient"); 
     reader.Read((r) => 
     { 
      EdmEntityObject obj = new EdmEntityObject(type); 
      foreach (var prop in type.DeclaredProperties) 
      { 
       int index = r.GetOrdinal(prop.Name); 
       object value = r.GetValue(index); 
       if (Convert.IsDBNull(value)) 
       { 
        value = null; 
       } 
       obj.TrySetPropertyValue(prop.Name, value); 
      } 

      list.Add(obj); 
      // uncomment these 2 lines if you're just testing maximum 10 rows on a table 
      //if (list.Count == 10) 
      // return false; 

      return true; 
     }); 
     return list; 
    } 

    public static IEdmModel GetEdmModel(string connectionString, TableControllerSelector selector) 
    { 
     EdmModel model = new EdmModel(); 

     // create and add entity container 
     EdmEntityContainer container = new EdmEntityContainer("NS", "DefaultContainer"); 
     model.AddElement(container); 

     // https://www.nuget.org/packages/DatabaseSchemaReader/ 
     using (var dbReader = new DatabaseReader(connectionString, "System.Data.SqlClient")) 
     { 
      var schema = dbReader.ReadAll(); 
      foreach (var table in schema.Tables) 
      { 
       EdmEntityType tableType = new EdmEntityType("NS", table.Name); 
       foreach (var col in table.Columns) 
       { 
        var kind = GetKind(col); 
        if (!kind.HasValue) // don't map this 
         continue; 

        var prop = tableType.AddStructuralProperty(col.Name, kind.Value, col.Nullable); 
        if (col.IsPrimaryKey) 
        { 
         tableType.AddKeys(prop); 
        } 
       } 
       model.AddElement(tableType); 

       EdmEntitySet products = container.AddEntitySet(table.Name, tableType); 
       selector.AddTable(connectionString, table); 
      } 
     } 

     return model; 
    } 

    // determine Edm kind from column type 
    private static EdmPrimitiveTypeKind? GetKind(DatabaseColumn col) 
    { 
     var dt = col.DataType; 
     if (col.DataType == null) 
      return null; 

     Type type = col.DataType.GetNetType(); 
     if (type == null) 
      return null; 

     if (type == typeof(string)) 
      return EdmPrimitiveTypeKind.String; 

     if (type == typeof(short)) 
      return EdmPrimitiveTypeKind.Int16; 

     if (type == typeof(int)) 
      return EdmPrimitiveTypeKind.Int32; 

     if (type == typeof(long)) 
      return EdmPrimitiveTypeKind.Int64; 

     if (type == typeof(bool)) 
      return EdmPrimitiveTypeKind.Boolean; 

     if (type == typeof(Guid)) 
      return EdmPrimitiveTypeKind.Guid; 

     if (type == typeof(DateTime)) 
      return EdmPrimitiveTypeKind.DateTimeOffset; 

     if (type == typeof(TimeSpan)) 
      return EdmPrimitiveTypeKind.Duration; 

     if (type == typeof(decimal)) 
      return EdmPrimitiveTypeKind.Decimal; 

     if (type == typeof(byte) || type == typeof(sbyte)) 
      return EdmPrimitiveTypeKind.Byte; 

     if (type == typeof(byte[])) 
      return EdmPrimitiveTypeKind.Binary; 

     if (type == typeof(double)) 
      return EdmPrimitiveTypeKind.Double; 

     if (type == typeof(float)) 
      return EdmPrimitiveTypeKind.Single; 

     return null; 
    } 
} 
+0

Ничего себе, это был именно «RosettaStone», который я искал! Благодаря! –

+0

Спасибо за этот образец! Простое примечание - в последних версиях, чтобы получить модель из запроса, вы должны использовать 'IEdmModel model = Request.GetModel();' вместо 'IEdmModel model = Request.ODataProperties(). Model;' (в TableController.Get()) –

+0

@ GonçaloBorrga - yep :-), это было также в моем комментарии здесь: https://github.com/OData/ODataSamples/issues/50#issuecomment-258799428 –

2

1. Вы пробовали использовать словарь? Я не знаю, работает ли он с OData, просто идея, которая возникла как все виды соединений/десеризаторов данных, с которыми я работал, также работал со словарем.

2. Идея я больше в, чтобы получить класс из источника данных, я нашел что-то здесь, что может помочь вам: Class to DataSet/DataSet to class, может быть, если не существует больше препятствий, используя данные, возвращаемые для создания структура для размещения данных внутри ...

Извините за то, что вы как-то не уверены в ответах, мне просто пришло в голову, что я мало знаю об OData. Надеюсь, это помогло.

Смежные вопросы